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