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 *
mpd_callocfunc_em(size_t nmemb,size_t size)56 mpd_callocfunc_em(size_t nmemb, size_t size)
57 {
58 void *ptr;
59 size_t req;
60 mpd_size_t overflow;
61
62 req = mul_size_t_overflow((mpd_size_t)nmemb, (mpd_size_t)size,
63 &overflow);
64 if (overflow) {
65 return NULL;
66 }
67
68 ptr = mpd_mallocfunc(req);
69 if (ptr == NULL) {
70 return NULL;
71 }
72 /* used on uint32_t or uint64_t */
73 memset(ptr, 0, req);
74
75 return ptr;
76 }
77
78
79 /* malloc with overflow checking */
80 void *
mpd_alloc(mpd_size_t nmemb,mpd_size_t size)81 mpd_alloc(mpd_size_t nmemb, mpd_size_t size)
82 {
83 mpd_size_t req, overflow;
84
85 req = mul_size_t_overflow(nmemb, size, &overflow);
86 if (overflow) {
87 return NULL;
88 }
89
90 return mpd_mallocfunc(req);
91 }
92
93 /* calloc with overflow checking */
94 void *
mpd_calloc(mpd_size_t nmemb,mpd_size_t size)95 mpd_calloc(mpd_size_t nmemb, mpd_size_t size)
96 {
97 mpd_size_t overflow;
98
99 (void)mul_size_t_overflow(nmemb, size, &overflow);
100 if (overflow) {
101 return NULL;
102 }
103
104 return mpd_callocfunc(nmemb, size);
105 }
106
107 /* realloc with overflow checking */
108 void *
mpd_realloc(void * ptr,mpd_size_t nmemb,mpd_size_t size,uint8_t * err)109 mpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err)
110 {
111 void *new;
112 mpd_size_t req, overflow;
113
114 req = mul_size_t_overflow(nmemb, size, &overflow);
115 if (overflow) {
116 *err = 1;
117 return ptr;
118 }
119
120 new = mpd_reallocfunc(ptr, req);
121 if (new == NULL) {
122 *err = 1;
123 return ptr;
124 }
125
126 return new;
127 }
128
129 /* struct hack malloc with overflow checking */
130 void *
mpd_sh_alloc(mpd_size_t struct_size,mpd_size_t nmemb,mpd_size_t size)131 mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size)
132 {
133 mpd_size_t req, overflow;
134
135 req = mul_size_t_overflow(nmemb, size, &overflow);
136 if (overflow) {
137 return NULL;
138 }
139
140 req = add_size_t_overflow(req, struct_size, &overflow);
141 if (overflow) {
142 return NULL;
143 }
144
145 return mpd_mallocfunc(req);
146 }
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 *
mpd_qnew_size(mpd_ssize_t nwords)152 mpd_qnew_size(mpd_ssize_t nwords)
153 {
154 mpd_t *result;
155
156 nwords = (nwords < MPD_MINALLOC) ? MPD_MINALLOC : nwords;
157
158 result = mpd_alloc(1, sizeof *result);
159 if (result == NULL) {
160 return NULL;
161 }
162
163 result->data = mpd_alloc(nwords, sizeof *result->data);
164 if (result->data == NULL) {
165 mpd_free(result);
166 return NULL;
167 }
168
169 result->flags = 0;
170 result->exp = 0;
171 result->digits = 0;
172 result->len = 0;
173 result->alloc = nwords;
174
175 return result;
176 }
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 *
mpd_qnew(void)181 mpd_qnew(void)
182 {
183 return mpd_qnew_size(MPD_MINALLOC);
184 }
185
186 /* Allocate new decimal. Caller can check for NULL or MPD_Malloc_error.
187 Raises on error. */
188 mpd_t *
mpd_new(mpd_context_t * ctx)189 mpd_new(mpd_context_t *ctx)
190 {
191 mpd_t *result;
192
193 result = mpd_qnew();
194 if (result == NULL) {
195 mpd_addstatus_raise(ctx, MPD_Malloc_error);
196 }
197 return result;
198 }
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
mpd_switch_to_dyn(mpd_t * result,mpd_ssize_t nwords,uint32_t * status)209 mpd_switch_to_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
210 {
211 mpd_uint_t *p = result->data;
212
213 assert(nwords >= result->alloc);
214
215 result->data = mpd_alloc(nwords, sizeof *result->data);
216 if (result->data == NULL) {
217 result->data = p;
218 mpd_set_qnan(result);
219 mpd_set_positive(result);
220 result->exp = result->digits = result->len = 0;
221 *status |= MPD_Malloc_error;
222 return 0;
223 }
224
225 memcpy(result->data, p, result->alloc * (sizeof *result->data));
226 result->alloc = nwords;
227 mpd_set_dynamic_data(result);
228 return 1;
229 }
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
mpd_switch_to_dyn_zero(mpd_t * result,mpd_ssize_t nwords,uint32_t * status)238 mpd_switch_to_dyn_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
239 {
240 mpd_uint_t *p = result->data;
241
242 result->data = mpd_calloc(nwords, sizeof *result->data);
243 if (result->data == NULL) {
244 result->data = p;
245 mpd_set_qnan(result);
246 mpd_set_positive(result);
247 result->exp = result->digits = result->len = 0;
248 *status |= MPD_Malloc_error;
249 return 0;
250 }
251
252 result->alloc = nwords;
253 mpd_set_dynamic_data(result);
254
255 return 1;
256 }
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
mpd_realloc_dyn(mpd_t * result,mpd_ssize_t nwords,uint32_t * status)273 mpd_realloc_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
274 {
275 uint8_t err = 0;
276
277 result->data = mpd_realloc(result->data, nwords, sizeof *result->data, &err);
278 if (!err) {
279 result->alloc = nwords;
280 }
281 else if (nwords > result->alloc) {
282 mpd_set_qnan(result);
283 mpd_set_positive(result);
284 result->exp = result->digits = result->len = 0;
285 *status |= MPD_Malloc_error;
286 return 0;
287 }
288
289 return 1;
290 }
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
mpd_switch_to_dyn_cxx(mpd_t * result,mpd_ssize_t nwords)302 mpd_switch_to_dyn_cxx(mpd_t *result, mpd_ssize_t nwords)
303 {
304 assert(nwords >= result->alloc);
305
306 mpd_uint_t *data = mpd_alloc(nwords, sizeof *result->data);
307 if (data == NULL) {
308 return 0;
309 }
310
311 memcpy(data, result->data, result->alloc * (sizeof *result->data));
312 result->data = data;
313 result->alloc = nwords;
314 mpd_set_dynamic_data(result);
315 return 1;
316 }
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
mpd_realloc_dyn_cxx(mpd_t * result,mpd_ssize_t nwords)333 mpd_realloc_dyn_cxx(mpd_t *result, mpd_ssize_t nwords)
334 {
335 uint8_t err = 0;
336
337 mpd_uint_t *p = mpd_realloc(result->data, nwords, sizeof *result->data, &err);
338 if (!err) {
339 result->data = p;
340 result->alloc = nwords;
341 }
342 else if (nwords > result->alloc) {
343 return 0;
344 }
345
346 return 1;
347 }
348