/src/mpdecimal-4.0.0/libmpdec/mpalloc.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2008-2024 Stefan Krah. All rights reserved. |
3 | | * |
4 | | * Redistribution and use in source and binary forms, with or without |
5 | | * modification, are permitted provided that the following conditions |
6 | | * are met: |
7 | | * |
8 | | * 1. Redistributions of source code must retain the above copyright |
9 | | * notice, this list of conditions and the following disclaimer. |
10 | | * 2. Redistributions in binary form must reproduce the above copyright |
11 | | * notice, this list of conditions and the following disclaimer in the |
12 | | * documentation and/or other materials provided with the distribution. |
13 | | * |
14 | | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
15 | | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 | | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
17 | | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
18 | | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
19 | | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
20 | | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
21 | | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
22 | | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
23 | | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
24 | | * SUCH DAMAGE. |
25 | | */ |
26 | | |
27 | | |
28 | | #include <assert.h> |
29 | | #include <stdio.h> |
30 | | #include <stdlib.h> |
31 | | #include <string.h> |
32 | | |
33 | | #include "mpalloc.h" |
34 | | #include "mpdecimal.h" |
35 | | #include "typearith.h" |
36 | | |
37 | | |
38 | | #if defined(_MSC_VER) |
39 | | #pragma warning(disable : 4232) |
40 | | #endif |
41 | | |
42 | | |
43 | | /* Guaranteed minimum allocation for a coefficient. May be changed once |
44 | | at program start using mpd_setminalloc(). */ |
45 | | mpd_ssize_t MPD_MINALLOC = MPD_MINALLOC_MIN; |
46 | | |
47 | | /* Custom allocation and free functions */ |
48 | | void *(* mpd_mallocfunc)(size_t size) = malloc; |
49 | | void *(* mpd_reallocfunc)(void *ptr, size_t size) = realloc; |
50 | | void *(* mpd_callocfunc)(size_t nmemb, size_t size) = calloc; |
51 | | void (* mpd_free)(void *ptr) = free; |
52 | | |
53 | | |
54 | | /* emulate calloc if it is not available */ |
55 | | void * |
56 | | mpd_callocfunc_em(size_t nmemb, size_t size) |
57 | 0 | { |
58 | 0 | void *ptr; |
59 | 0 | size_t req; |
60 | 0 | mpd_size_t overflow; |
61 | |
|
62 | 0 | req = mul_size_t_overflow((mpd_size_t)nmemb, (mpd_size_t)size, |
63 | 0 | &overflow); |
64 | 0 | if (overflow) { |
65 | 0 | return NULL; |
66 | 0 | } |
67 | | |
68 | 0 | ptr = mpd_mallocfunc(req); |
69 | 0 | if (ptr == NULL) { |
70 | 0 | return NULL; |
71 | 0 | } |
72 | | /* used on uint32_t or uint64_t */ |
73 | 0 | memset(ptr, 0, req); |
74 | |
|
75 | 0 | return ptr; |
76 | 0 | } |
77 | | |
78 | | |
79 | | /* malloc with overflow checking */ |
80 | | void * |
81 | | mpd_alloc(mpd_size_t nmemb, mpd_size_t size) |
82 | 1.96M | { |
83 | 1.96M | mpd_size_t req, overflow; |
84 | | |
85 | 1.96M | req = mul_size_t_overflow(nmemb, size, &overflow); |
86 | 1.96M | if (overflow) { |
87 | 0 | return NULL; |
88 | 0 | } |
89 | | |
90 | 1.96M | return mpd_mallocfunc(req); |
91 | 1.96M | } |
92 | | |
93 | | /* calloc with overflow checking */ |
94 | | void * |
95 | | mpd_calloc(mpd_size_t nmemb, mpd_size_t size) |
96 | 1.61M | { |
97 | 1.61M | mpd_size_t overflow; |
98 | | |
99 | 1.61M | (void)mul_size_t_overflow(nmemb, size, &overflow); |
100 | 1.61M | if (overflow) { |
101 | 0 | return NULL; |
102 | 0 | } |
103 | | |
104 | 1.61M | return mpd_callocfunc(nmemb, size); |
105 | 1.61M | } |
106 | | |
107 | | /* realloc with overflow checking */ |
108 | | void * |
109 | | mpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err) |
110 | 2.12M | { |
111 | 2.12M | void *new; |
112 | 2.12M | mpd_size_t req, overflow; |
113 | | |
114 | 2.12M | req = mul_size_t_overflow(nmemb, size, &overflow); |
115 | 2.12M | if (overflow) { |
116 | 0 | *err = 1; |
117 | 0 | return ptr; |
118 | 0 | } |
119 | | |
120 | 2.12M | new = mpd_reallocfunc(ptr, req); |
121 | 2.12M | if (new == NULL) { |
122 | 0 | *err = 1; |
123 | 0 | return ptr; |
124 | 0 | } |
125 | | |
126 | 2.12M | return new; |
127 | 2.12M | } |
128 | | |
129 | | /* struct hack malloc with overflow checking */ |
130 | | void * |
131 | | mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size) |
132 | 0 | { |
133 | 0 | mpd_size_t req, overflow; |
134 | |
|
135 | 0 | req = mul_size_t_overflow(nmemb, size, &overflow); |
136 | 0 | if (overflow) { |
137 | 0 | return NULL; |
138 | 0 | } |
139 | | |
140 | 0 | req = add_size_t_overflow(req, struct_size, &overflow); |
141 | 0 | if (overflow) { |
142 | 0 | return NULL; |
143 | 0 | } |
144 | | |
145 | 0 | return mpd_mallocfunc(req); |
146 | 0 | } |
147 | | |
148 | | |
149 | | /* Allocate a new decimal with a coefficient of length 'nwords'. In case |
150 | | of an error the return value is NULL. */ |
151 | | mpd_t * |
152 | | mpd_qnew_size(mpd_ssize_t nwords) |
153 | 167k | { |
154 | 167k | mpd_t *result; |
155 | | |
156 | 167k | nwords = (nwords < MPD_MINALLOC) ? MPD_MINALLOC : nwords; |
157 | | |
158 | 167k | result = mpd_alloc(1, sizeof *result); |
159 | 167k | if (result == NULL) { |
160 | 0 | return NULL; |
161 | 0 | } |
162 | | |
163 | 167k | result->data = mpd_alloc(nwords, sizeof *result->data); |
164 | 167k | if (result->data == NULL) { |
165 | 0 | mpd_free(result); |
166 | 0 | return NULL; |
167 | 0 | } |
168 | | |
169 | 167k | result->flags = 0; |
170 | 167k | result->exp = 0; |
171 | 167k | result->digits = 0; |
172 | 167k | result->len = 0; |
173 | 167k | result->alloc = nwords; |
174 | | |
175 | 167k | return result; |
176 | 167k | } |
177 | | |
178 | | /* Allocate a new decimal with a coefficient of length MPD_MINALLOC. |
179 | | In case of an error the return value is NULL. */ |
180 | | mpd_t * |
181 | | mpd_qnew(void) |
182 | 167k | { |
183 | 167k | return mpd_qnew_size(MPD_MINALLOC); |
184 | 167k | } |
185 | | |
186 | | /* Allocate new decimal. Caller can check for NULL or MPD_Malloc_error. |
187 | | Raises on error. */ |
188 | | mpd_t * |
189 | | mpd_new(mpd_context_t *ctx) |
190 | 167k | { |
191 | 167k | mpd_t *result; |
192 | | |
193 | 167k | result = mpd_qnew(); |
194 | 167k | if (result == NULL) { |
195 | 0 | mpd_addstatus_raise(ctx, MPD_Malloc_error); |
196 | 0 | } |
197 | 167k | return result; |
198 | 167k | } |
199 | | |
200 | | /* |
201 | | * Input: 'result' is a static mpd_t with a static coefficient. |
202 | | * Assumption: 'nwords' >= result->alloc. |
203 | | * |
204 | | * Resize the static coefficient to a larger dynamic one and copy the |
205 | | * existing data. If successful, the value of 'result' is unchanged. |
206 | | * Otherwise, set 'result' to NaN and update 'status' with MPD_Malloc_error. |
207 | | */ |
208 | | int |
209 | | mpd_switch_to_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) |
210 | 194 | { |
211 | 194 | mpd_uint_t *p = result->data; |
212 | | |
213 | 194 | assert(nwords >= result->alloc); |
214 | | |
215 | 194 | result->data = mpd_alloc(nwords, sizeof *result->data); |
216 | 194 | if (result->data == NULL) { |
217 | 0 | result->data = p; |
218 | 0 | mpd_set_qnan(result); |
219 | 0 | mpd_set_positive(result); |
220 | 0 | result->exp = result->digits = result->len = 0; |
221 | 0 | *status |= MPD_Malloc_error; |
222 | 0 | return 0; |
223 | 0 | } |
224 | | |
225 | 194 | memcpy(result->data, p, result->alloc * (sizeof *result->data)); |
226 | 194 | result->alloc = nwords; |
227 | 194 | mpd_set_dynamic_data(result); |
228 | 194 | return 1; |
229 | 194 | } |
230 | | |
231 | | /* |
232 | | * Input: 'result' is a static mpd_t with a static coefficient. |
233 | | * |
234 | | * Convert the coefficient to a dynamic one that is initialized to zero. If |
235 | | * malloc fails, set 'result' to NaN and update 'status' with MPD_Malloc_error. |
236 | | */ |
237 | | int |
238 | | mpd_switch_to_dyn_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) |
239 | 0 | { |
240 | 0 | mpd_uint_t *p = result->data; |
241 | |
|
242 | 0 | result->data = mpd_calloc(nwords, sizeof *result->data); |
243 | 0 | if (result->data == NULL) { |
244 | 0 | result->data = p; |
245 | 0 | mpd_set_qnan(result); |
246 | 0 | mpd_set_positive(result); |
247 | 0 | result->exp = result->digits = result->len = 0; |
248 | 0 | *status |= MPD_Malloc_error; |
249 | 0 | return 0; |
250 | 0 | } |
251 | | |
252 | 0 | result->alloc = nwords; |
253 | 0 | mpd_set_dynamic_data(result); |
254 | |
|
255 | 0 | return 1; |
256 | 0 | } |
257 | | |
258 | | /* |
259 | | * Input: 'result' is a static or a dynamic mpd_t with a dynamic coefficient. |
260 | | * Resize the coefficient to length 'nwords': |
261 | | * Case nwords > result->alloc: |
262 | | * If realloc is successful: |
263 | | * 'result' has a larger coefficient but the same value. Return 1. |
264 | | * Otherwise: |
265 | | * Set 'result' to NaN, update status with MPD_Malloc_error and return 0. |
266 | | * Case nwords < result->alloc: |
267 | | * If realloc is successful: |
268 | | * 'result' has a smaller coefficient. result->len is undefined. Return 1. |
269 | | * Otherwise (unlikely): |
270 | | * 'result' is unchanged. Reuse the now oversized coefficient. Return 1. |
271 | | */ |
272 | | int |
273 | | mpd_realloc_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) |
274 | 2.12M | { |
275 | 2.12M | uint8_t err = 0; |
276 | | |
277 | 2.12M | result->data = mpd_realloc(result->data, nwords, sizeof *result->data, &err); |
278 | 2.12M | if (!err) { |
279 | 2.12M | result->alloc = nwords; |
280 | 2.12M | } |
281 | 0 | else if (nwords > result->alloc) { |
282 | 0 | mpd_set_qnan(result); |
283 | 0 | mpd_set_positive(result); |
284 | 0 | result->exp = result->digits = result->len = 0; |
285 | 0 | *status |= MPD_Malloc_error; |
286 | 0 | return 0; |
287 | 0 | } |
288 | | |
289 | 2.12M | return 1; |
290 | 2.12M | } |
291 | | |
292 | | /* |
293 | | * Input: 'result' is a static mpd_t with a static coefficient. |
294 | | * Assumption: 'nwords' >= result->alloc. |
295 | | * |
296 | | * Resize the static coefficient to a larger dynamic one and copy the |
297 | | * existing data. |
298 | | * |
299 | | * On failure the value of 'result' is unchanged. |
300 | | */ |
301 | | int |
302 | | mpd_switch_to_dyn_cxx(mpd_t *result, mpd_ssize_t nwords) |
303 | 0 | { |
304 | 0 | assert(nwords >= result->alloc); |
305 | |
|
306 | 0 | mpd_uint_t *data = mpd_alloc(nwords, sizeof *result->data); |
307 | 0 | if (data == NULL) { |
308 | 0 | return 0; |
309 | 0 | } |
310 | | |
311 | 0 | memcpy(data, result->data, result->alloc * (sizeof *result->data)); |
312 | 0 | result->data = data; |
313 | 0 | result->alloc = nwords; |
314 | 0 | mpd_set_dynamic_data(result); |
315 | 0 | return 1; |
316 | 0 | } |
317 | | |
318 | | /* |
319 | | * Input: 'result' is a static or a dynamic mpd_t with a dynamic coefficient. |
320 | | * Resize the coefficient to length 'nwords': |
321 | | * Case nwords > result->alloc: |
322 | | * If realloc is successful: |
323 | | * 'result' has a larger coefficient but the same value. Return 1. |
324 | | * Otherwise: |
325 | | * 'result' has a the same coefficient. Return 0. |
326 | | * Case nwords < result->alloc: |
327 | | * If realloc is successful: |
328 | | * 'result' has a smaller coefficient. result->len is undefined. Return 1. |
329 | | * Otherwise (unlikely): |
330 | | * 'result' is unchanged. Reuse the now oversized coefficient. Return 1. |
331 | | */ |
332 | | int |
333 | | mpd_realloc_dyn_cxx(mpd_t *result, mpd_ssize_t nwords) |
334 | 0 | { |
335 | 0 | uint8_t err = 0; |
336 | |
|
337 | 0 | mpd_uint_t *p = mpd_realloc(result->data, nwords, sizeof *result->data, &err); |
338 | 0 | if (!err) { |
339 | 0 | result->data = p; |
340 | 0 | result->alloc = nwords; |
341 | 0 | } |
342 | 0 | else if (nwords > result->alloc) { |
343 | 0 | return 0; |
344 | 0 | } |
345 | | |
346 | 0 | return 1; |
347 | 0 | } |