ohmu
·
2026-10-15
1/*
2 * Copyright (c) 2014-2018 Hayaki Saito
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 * this software and associated documentation files (the "Software"), to deal in
6 * the Software without restriction, including without limitation the rights to
7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8 * the Software, and to permit persons to whom the Software is furnished to do so,
9 * subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 */
21
22#include "config.h"
23
24/* STDC_HEADERS */
25#include <stdlib.h>
26
27#if HAVE_ASSERT_H
28# include <assert.h>
29#endif /* HAVE_ASSERT_H */
30#if HAVE_SYS_TYPES_H
31# include <sys/types.h>
32#endif /* HAVE_SYS_TYPES_H */
33#if HAVE_ERRNO_H
34# include <errno.h>
35#endif /* HAVE_ERRNO_H */
36#if HAVE_MEMORY_H
37# include <memory.h>
38#endif /* HAVE_MEMORY_H */
39
40#include "allocator.h"
41#include "malloc_stub.h"
42
43/* create allocator object */
44SIXELSTATUS
45sixel_allocator_new(
46 sixel_allocator_t /* out */ **ppallocator, /* allocator object to be created */
47 sixel_malloc_t /* in */ fn_malloc, /* custom malloc() function */
48 sixel_calloc_t /* in */ fn_calloc, /* custom calloc() function */
49 sixel_realloc_t /* in */ fn_realloc, /* custom realloc() function */
50 sixel_free_t /* in */ fn_free) /* custom free() function */
51{
52 SIXELSTATUS status = SIXEL_FALSE;
53
54 if (ppallocator == NULL) {
55 sixel_helper_set_additional_message(
56 "sixel_allocator_new: given argument ppallocator is null.");
57 status = SIXEL_BAD_ARGUMENT;
58 goto end;
59 }
60
61 if (fn_malloc == NULL) {
62 fn_malloc = malloc;
63 }
64
65 if (fn_calloc == NULL) {
66 fn_calloc = calloc;
67 }
68
69 if (fn_realloc == NULL) {
70 fn_realloc = realloc;
71 }
72
73 if (fn_free == NULL) {
74 fn_free = free;
75 }
76
77 *ppallocator = fn_malloc(sizeof(sixel_allocator_t));
78 if (*ppallocator == NULL) {
79 sixel_helper_set_additional_message(
80 "sixel_allocator_new: fn_malloc() failed.");
81 status = SIXEL_BAD_ALLOCATION;
82 goto end;
83 }
84
85 (*ppallocator)->ref = 1;
86 (*ppallocator)->fn_malloc = fn_malloc;
87 (*ppallocator)->fn_calloc = fn_calloc;
88 (*ppallocator)->fn_realloc = fn_realloc;
89 (*ppallocator)->fn_free = fn_free;
90
91 status = SIXEL_OK;
92
93end:
94 return status;
95}
96
97
98/* destruct allocator object */
99static void
100sixel_allocator_destroy(
101 sixel_allocator_t /* in */ *allocator) /* allocator object to
102 be destroyed */
103{
104 /* precondition */
105 assert(allocator);
106 assert(allocator->fn_free);
107
108 allocator->fn_free(allocator);
109}
110
111
112/* increase reference count of allocatort object (thread-unsafe) */
113SIXELAPI void
114sixel_allocator_ref(
115 sixel_allocator_t /* in */ *allocator) /* allocator object to be
116 increment reference counter */
117{
118 /* precondition */
119 assert(allocator);
120
121 /* TODO: be thread safe */
122 ++allocator->ref;
123}
124
125
126/* decrease reference count of output context object (thread-unsafe) */
127SIXELAPI void
128sixel_allocator_unref(
129 sixel_allocator_t /* in */ *allocator) /* allocator object to be unreference */
130{
131 /* TODO: be thread safe */
132 if (allocator) {
133 assert(allocator->ref > 0);
134 --allocator->ref;
135 if (allocator->ref == 0) {
136 sixel_allocator_destroy(allocator);
137 }
138 }
139}
140
141
142/* call custom malloc() */
143SIXELAPI void *
144sixel_allocator_malloc(
145 sixel_allocator_t /* in */ *allocator, /* allocator object */
146 size_t /* in */ n) /* allocation size */
147{
148 /* precondition */
149 assert(allocator);
150 assert(allocator->fn_malloc);
151
152 if (n == 0) {
153 sixel_helper_set_additional_message(
154 "sixel_allocator_malloc: called with n == 0");
155 return NULL;
156 }
157
158 if (n > SIXEL_ALLOCATE_BYTES_MAX) {
159 return NULL;
160 }
161
162 return allocator->fn_malloc(n);
163}
164
165
166/* call custom calloc() */
167SIXELAPI void *
168sixel_allocator_calloc(
169 sixel_allocator_t /* in */ *allocator, /* allocator object */
170 size_t /* in */ nelm, /* number of elements */
171 size_t /* in */ elsize) /* size of element */
172{
173 size_t n;
174
175 /* precondition */
176 assert(allocator);
177 assert(allocator->fn_calloc);
178
179 n = nelm * elsize;
180
181 if (n == 0) {
182 sixel_helper_set_additional_message(
183 "sixel_allocator_malloc: called with n == 0");
184 return NULL;
185 }
186
187 if (n > SIXEL_ALLOCATE_BYTES_MAX) {
188 return NULL;
189 }
190
191 return allocator->fn_calloc(nelm, elsize);
192}
193
194
195/* call custom realloc() */
196SIXELAPI void *
197sixel_allocator_realloc(
198 sixel_allocator_t /* in */ *allocator, /* allocator object */
199 void /* in */ *p, /* existing buffer to be re-allocated */
200 size_t /* in */ n) /* re-allocation size */
201{
202 /* precondition */
203 assert(allocator);
204 assert(allocator->fn_realloc);
205
206 if (n == 0) {
207 sixel_helper_set_additional_message(
208 "sixel_allocator_malloc: called with n == 0");
209 return NULL;
210 }
211
212 if (n > SIXEL_ALLOCATE_BYTES_MAX) {
213 return NULL;
214 }
215
216 return allocator->fn_realloc(p, n);
217}
218
219
220/* call custom free() */
221SIXELAPI void
222sixel_allocator_free(
223 sixel_allocator_t /* in */ *allocator, /* allocator object */
224 void /* in */ *p) /* existing buffer to be freed */
225{
226 /* precondition */
227 assert(allocator);
228 assert(allocator->fn_free);
229
230 allocator->fn_free(p);
231}
232
233
234#if HAVE_TESTS
235volatile int sixel_debug_malloc_counter;
236
237void *
238sixel_bad_malloc(size_t size)
239{
240 return sixel_debug_malloc_counter-- == 0 ? NULL: malloc(size);
241}
242
243
244void *
245sixel_bad_calloc(size_t count, size_t size)
246{
247 (void) count;
248 (void) size;
249
250 return NULL;
251}
252
253
254void *
255sixel_bad_realloc(void *ptr, size_t size)
256{
257 (void) ptr;
258 (void) size;
259
260 return NULL;
261}
262#endif /* HAVE_TESTS */
263
264#if 0
265int
266rpl_posix_memalign(void **memptr, size_t alignment, size_t size)
267{
268#if HAVE_POSIX_MEMALIGN
269 return posix_memalign(memptr, alignment, size);
270#elif HAVE_ALIGNED_ALLOC
271 *memptr = aligned_alloc(alignment, size);
272 return *memptr ? 0: ENOMEM;
273#elif HAVE_MEMALIGN
274 *memptr = memalign(alignment, size);
275 return *memptr ? 0: ENOMEM;
276#elif HAVE__ALIGNED_MALLOC
277 return _aligned_malloc(size, alignment);
278#else
279# error
280#endif /* _MSC_VER */
281}
282#endif
283
284
285#if HAVE_TESTS
286static int
287test1(void)
288{
289 int nret = EXIT_FAILURE;
290 SIXELSTATUS status;
291 sixel_allocator_t *allocator = NULL;
292
293 status = sixel_allocator_new(NULL, malloc, calloc, realloc, free);
294 if (status != SIXEL_BAD_ARGUMENT) {
295 goto error;
296 }
297
298 status = sixel_allocator_new(&allocator, NULL, calloc, realloc, free);
299 if (SIXEL_FAILED(status)) {
300 goto error;
301 }
302
303 status = sixel_allocator_new(&allocator, malloc, NULL, realloc, free);
304 if (SIXEL_FAILED(status)) {
305 goto error;
306 }
307
308 status = sixel_allocator_new(&allocator, malloc, calloc, NULL, free);
309 if (SIXEL_FAILED(status)) {
310 goto error;
311 }
312
313 status = sixel_allocator_new(&allocator, malloc, calloc, realloc, NULL);
314 if (SIXEL_FAILED(status)) {
315 goto error;
316 }
317
318 nret = EXIT_SUCCESS;
319
320error:
321 return nret;
322}
323
324
325static int
326test2(void)
327{
328 int nret = EXIT_FAILURE;
329 SIXELSTATUS status;
330 sixel_allocator_t *allocator = NULL;
331
332 sixel_debug_malloc_counter = 1;
333
334 status = sixel_allocator_new(&allocator, sixel_bad_malloc, calloc, realloc, free);
335 if (status == SIXEL_BAD_ALLOCATION) {
336 goto error;
337 }
338
339 nret = EXIT_SUCCESS;
340
341error:
342 return nret;
343}
344
345
346SIXELAPI int
347sixel_allocator_tests_main(void)
348{
349 int nret = EXIT_FAILURE;
350 size_t i;
351 typedef int (* testcase)(void);
352
353 static testcase const testcases[] = {
354 test1,
355 test2
356 };
357
358 for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
359 nret = testcases[i]();
360 if (nret != EXIT_SUCCESS) {
361 goto error;
362 }
363 }
364
365 nret = EXIT_SUCCESS;
366
367error:
368 return nret;
369}
370#endif /* HAVE_TESTS */
371
372/* emacs Local Variables: */
373/* emacs mode: c */
374/* emacs tab-width: 4 */
375/* emacs indent-tabs-mode: nil */
376/* emacs c-basic-offset: 4 */
377/* emacs End: */
378/* vim: set expandtab ts=4 sts=4 sw=4 : */
379/* EOF */