• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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