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