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