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(). */
47mpd_ssize_t MPD_MINALLOC = MPD_MINALLOC_MIN;
48
49/* Custom allocation and free functions */
50void *(* mpd_mallocfunc)(size_t size) = malloc;
51void *(* mpd_reallocfunc)(void *ptr, size_t size) = realloc;
52void *(* mpd_callocfunc)(size_t nmemb, size_t size) = calloc;
53void (* mpd_free)(void *ptr) = free;
54
55
56/* emulate calloc if it is not available */
57void *
58mpd_callocfunc_em(size_t nmemb, size_t size)
59{
60 void *ptr;
61 size_t req;
62 mpd_size_t overflow;
63
64 req = mul_size_t_overflow((mpd_size_t)nmemb, (mpd_size_t)size,
65 &overflow);
66 if (overflow) {
67 return NULL;
68 }
69
70 ptr = mpd_mallocfunc(req);
71 if (ptr == NULL) {
72 return NULL;
73 }
74 /* used on uint32_t or uint64_t */
75 memset(ptr, 0, req);
76
77 return ptr;
78}
79
80
81/* malloc with overflow checking */
82void *
83mpd_alloc(mpd_size_t nmemb, mpd_size_t size)
84{
85 mpd_size_t req, overflow;
86
87 req = mul_size_t_overflow(nmemb, size, &overflow);
88 if (overflow) {
89 return NULL;
90 }
91
92 return mpd_mallocfunc(req);
93}
94
95/* calloc with overflow checking */
96void *
97mpd_calloc(mpd_size_t nmemb, mpd_size_t size)
98{
99 mpd_size_t overflow;
100
101 (void)mul_size_t_overflow(nmemb, size, &overflow);
102 if (overflow) {
103 return NULL;
104 }
105
106 return mpd_callocfunc(nmemb, size);
107}
108
109/* realloc with overflow checking */
110void *
111mpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err)
112{
113 void *new;
114 mpd_size_t req, overflow;
115
116 req = mul_size_t_overflow(nmemb, size, &overflow);
117 if (overflow) {
118 *err = 1;
119 return ptr;
120 }
121
122 new = mpd_reallocfunc(ptr, req);
123 if (new == NULL) {
124 *err = 1;
125 return ptr;
126 }
127
128 return new;
129}
130
131/* struct hack malloc with overflow checking */
132void *
133mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size)
134{
135 mpd_size_t req, overflow;
136
137 req = mul_size_t_overflow(nmemb, size, &overflow);
138 if (overflow) {
139 return NULL;
140 }
141
142 req = add_size_t_overflow(req, struct_size, &overflow);
143 if (overflow) {
144 return NULL;
145 }
146
147 return mpd_mallocfunc(req);
148}
149
150
151/* Allocate a new decimal with a coefficient of length 'nwords'. In case
152 of an error the return value is NULL. */
153mpd_t *
154mpd_qnew_size(mpd_ssize_t nwords)
155{
156 mpd_t *result;
157
158 nwords = (nwords < MPD_MINALLOC) ? MPD_MINALLOC : nwords;
159
160 result = mpd_alloc(1, sizeof *result);
161 if (result == NULL) {
162 return NULL;
163 }
164
165 result->data = mpd_alloc(nwords, sizeof *result->data);
166 if (result->data == NULL) {
167 mpd_free(result);
168 return NULL;
169 }
170
171 result->flags = 0;
172 result->exp = 0;
173 result->digits = 0;
174 result->len = 0;
175 result->alloc = nwords;
176
177 return result;
178}
179
180/* Allocate a new decimal with a coefficient of length MPD_MINALLOC.
181 In case of an error the return value is NULL. */
182mpd_t *
183mpd_qnew(void)
184{
185 return mpd_qnew_size(MPD_MINALLOC);
186}
187
188/* Allocate new decimal. Caller can check for NULL or MPD_Malloc_error.
189 Raises on error. */
190mpd_t *
191mpd_new(mpd_context_t *ctx)
192{
193 mpd_t *result;
194
195 result = mpd_qnew();
196 if (result == NULL) {
197 mpd_addstatus_raise(ctx, MPD_Malloc_error);
198 }
199 return result;
200}
201
202/*
203 * Input: 'result' is a static mpd_t with a static coefficient.
204 * Assumption: 'nwords' >= result->alloc.
205 *
206 * Resize the static coefficient to a larger dynamic one and copy the
207 * existing data. If successful, the value of 'result' is unchanged.
208 * Otherwise, set 'result' to NaN and update 'status' with MPD_Malloc_error.
209 */
210int
211mpd_switch_to_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
212{
213 mpd_uint_t *p = result->data;
214
215 assert(nwords >= result->alloc);
216
217 result->data = mpd_alloc(nwords, sizeof *result->data);
218 if (result->data == NULL) {
219 result->data = p;
220 mpd_set_qnan(result);
221 mpd_set_positive(result);
222 result->exp = result->digits = result->len = 0;
223 *status |= MPD_Malloc_error;
224 return 0;
225 }
226
227 memcpy(result->data, p, result->alloc * (sizeof *result->data));
228 result->alloc = nwords;
229 mpd_set_dynamic_data(result);
230 return 1;
231}
232
233/*
234 * Input: 'result' is a static mpd_t with a static coefficient.
235 *
236 * Convert the coefficient to a dynamic one that is initialized to zero. If
237 * malloc fails, set 'result' to NaN and update 'status' with MPD_Malloc_error.
238 */
239int
240mpd_switch_to_dyn_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
241{
242 mpd_uint_t *p = result->data;
243
244 result->data = mpd_calloc(nwords, sizeof *result->data);
245 if (result->data == NULL) {
246 result->data = p;
247 mpd_set_qnan(result);
248 mpd_set_positive(result);
249 result->exp = result->digits = result->len = 0;
250 *status |= MPD_Malloc_error;
251 return 0;
252 }
253
254 result->alloc = nwords;
255 mpd_set_dynamic_data(result);
256
257 return 1;
258}
259
260/*
261 * Input: 'result' is a static or a dynamic mpd_t with a dynamic coefficient.
262 * Resize the coefficient to length 'nwords':
263 * Case nwords > result->alloc:
264 * If realloc is successful:
265 * 'result' has a larger coefficient but the same value. Return 1.
266 * Otherwise:
267 * Set 'result' to NaN, update status with MPD_Malloc_error and return 0.
268 * Case nwords < result->alloc:
269 * If realloc is successful:
270 * 'result' has a smaller coefficient. result->len is undefined. Return 1.
271 * Otherwise (unlikely):
272 * 'result' is unchanged. Reuse the now oversized coefficient. Return 1.
273 */
274int
275mpd_realloc_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
276{
277 uint8_t err = 0;
278
279 result->data = mpd_realloc(result->data, nwords, sizeof *result->data, &err);
280 if (!err) {
281 result->alloc = nwords;
282 }
283 else if (nwords > result->alloc) {
284 mpd_set_qnan(result);
285 mpd_set_positive(result);
286 result->exp = result->digits = result->len = 0;
287 *status |= MPD_Malloc_error;
288 return 0;
289 }
290
291 return 1;
292}
293
294/*
295 * Input: 'result' is a static mpd_t with a static coefficient.
296 * Assumption: 'nwords' >= result->alloc.
297 *
298 * Resize the static coefficient to a larger dynamic one and copy the
299 * existing data.
300 *
301 * On failure the value of 'result' is unchanged.
302 */
303int
304mpd_switch_to_dyn_cxx(mpd_t *result, mpd_ssize_t nwords)
305{
306 assert(nwords >= result->alloc);
307
308 mpd_uint_t *data = mpd_alloc(nwords, sizeof *result->data);
309 if (data == NULL) {
310 return 0;
311 }
312
313 memcpy(data, result->data, result->alloc * (sizeof *result->data));
314 result->data = data;
315 result->alloc = nwords;
316 mpd_set_dynamic_data(result);
317 return 1;
318}
319
320/*
321 * Input: 'result' is a static or a dynamic mpd_t with a dynamic coefficient.
322 * Resize the coefficient to length 'nwords':
323 * Case nwords > result->alloc:
324 * If realloc is successful:
325 * 'result' has a larger coefficient but the same value. Return 1.
326 * Otherwise:
327 * 'result' has a the same coefficient. Return 0.
328 * Case nwords < result->alloc:
329 * If realloc is successful:
330 * 'result' has a smaller coefficient. result->len is undefined. Return 1.
331 * Otherwise (unlikely):
332 * 'result' is unchanged. Reuse the now oversized coefficient. Return 1.
333 */
334int
335mpd_realloc_dyn_cxx(mpd_t *result, mpd_ssize_t nwords)
336{
337 uint8_t err = 0;
338
339 mpd_uint_t *p = mpd_realloc(result->data, nwords, sizeof *result->data, &err);
340 if (!err) {
341 result->data = p;
342 result->alloc = nwords;
343 }
344 else if (nwords > result->alloc) {
345 return 0;
346 }
347
348 return 1;
349}
350