• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2008-2016 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 #include <stdio.h>
31 #include <stdlib.h>
32 #include "typearith.h"
33 #include "mpalloc.h"
34 
35 
36 #if defined(_MSC_VER)
37   #pragma warning(disable : 4232)
38 #endif
39 
40 
41 /* Guaranteed minimum allocation for a coefficient. May be changed once
42    at program start using mpd_setminalloc(). */
43 mpd_ssize_t MPD_MINALLOC = MPD_MINALLOC_MIN;
44 
45 /* Custom allocation and free functions */
46 void *(* mpd_mallocfunc)(size_t size) = malloc;
47 void *(* mpd_reallocfunc)(void *ptr, size_t size) = realloc;
48 void *(* mpd_callocfunc)(size_t nmemb, size_t size) = calloc;
49 void (* mpd_free)(void *ptr) = free;
50 
51 
52 /* emulate calloc if it is not available */
53 void *
mpd_callocfunc_em(size_t nmemb,size_t size)54 mpd_callocfunc_em(size_t nmemb, size_t size)
55 {
56     void *ptr;
57     size_t req;
58     mpd_size_t overflow;
59 
60 #if MPD_SIZE_MAX < SIZE_MAX
61     /* full_coverage test only */
62     if (nmemb > MPD_SIZE_MAX || size > MPD_SIZE_MAX) {
63         return NULL;
64     }
65 #endif
66 
67     req = mul_size_t_overflow((mpd_size_t)nmemb, (mpd_size_t)size,
68                               &overflow);
69     if (overflow) {
70         return NULL;
71     }
72 
73     ptr = mpd_mallocfunc(req);
74     if (ptr == NULL) {
75         return NULL;
76     }
77     /* used on uint32_t or uint64_t */
78     memset(ptr, 0, req);
79 
80     return ptr;
81 }
82 
83 
84 /* malloc with overflow checking */
85 void *
mpd_alloc(mpd_size_t nmemb,mpd_size_t size)86 mpd_alloc(mpd_size_t nmemb, mpd_size_t size)
87 {
88     mpd_size_t req, overflow;
89 
90     req = mul_size_t_overflow(nmemb, size, &overflow);
91     if (overflow) {
92         return NULL;
93     }
94 
95     return mpd_mallocfunc(req);
96 }
97 
98 /* calloc with overflow checking */
99 void *
mpd_calloc(mpd_size_t nmemb,mpd_size_t size)100 mpd_calloc(mpd_size_t nmemb, mpd_size_t size)
101 {
102     mpd_size_t overflow;
103 
104     (void)mul_size_t_overflow(nmemb, size, &overflow);
105     if (overflow) {
106         return NULL;
107     }
108 
109     return mpd_callocfunc(nmemb, size);
110 }
111 
112 /* realloc with overflow checking */
113 void *
mpd_realloc(void * ptr,mpd_size_t nmemb,mpd_size_t size,uint8_t * err)114 mpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err)
115 {
116     void *new;
117     mpd_size_t req, overflow;
118 
119     req = mul_size_t_overflow(nmemb, size, &overflow);
120     if (overflow) {
121         *err = 1;
122         return ptr;
123     }
124 
125     new = mpd_reallocfunc(ptr, req);
126     if (new == NULL) {
127         *err = 1;
128         return ptr;
129     }
130 
131     return new;
132 }
133 
134 /* struct hack malloc with overflow checking */
135 void *
mpd_sh_alloc(mpd_size_t struct_size,mpd_size_t nmemb,mpd_size_t size)136 mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size)
137 {
138     mpd_size_t req, overflow;
139 
140     req = mul_size_t_overflow(nmemb, size, &overflow);
141     if (overflow) {
142         return NULL;
143     }
144 
145     req = add_size_t_overflow(req, struct_size, &overflow);
146     if (overflow) {
147         return NULL;
148     }
149 
150     return mpd_mallocfunc(req);
151 }
152 
153 
154 /* Allocate a new decimal with a coefficient of length 'nwords'. In case
155    of an error the return value is NULL. */
156 mpd_t *
mpd_qnew_size(mpd_ssize_t nwords)157 mpd_qnew_size(mpd_ssize_t nwords)
158 {
159     mpd_t *result;
160 
161     nwords = (nwords < MPD_MINALLOC) ? MPD_MINALLOC : nwords;
162 
163     result = mpd_alloc(1, sizeof *result);
164     if (result == NULL) {
165         return NULL;
166     }
167 
168     result->data = mpd_alloc(nwords, sizeof *result->data);
169     if (result->data == NULL) {
170         mpd_free(result);
171         return NULL;
172     }
173 
174     result->flags = 0;
175     result->exp = 0;
176     result->digits = 0;
177     result->len = 0;
178     result->alloc = nwords;
179 
180     return result;
181 }
182 
183 /* Allocate a new decimal with a coefficient of length MPD_MINALLOC.
184    In case of an error the return value is NULL. */
185 mpd_t *
mpd_qnew(void)186 mpd_qnew(void)
187 {
188     return mpd_qnew_size(MPD_MINALLOC);
189 }
190 
191 /* Allocate new decimal. Caller can check for NULL or MPD_Malloc_error.
192    Raises on error. */
193 mpd_t *
mpd_new(mpd_context_t * ctx)194 mpd_new(mpd_context_t *ctx)
195 {
196     mpd_t *result;
197 
198     result = mpd_qnew();
199     if (result == NULL) {
200         mpd_addstatus_raise(ctx, MPD_Malloc_error);
201     }
202     return result;
203 }
204 
205 /*
206  * Input: 'result' is a static mpd_t with a static coefficient.
207  * Assumption: 'nwords' >= result->alloc.
208  *
209  * Resize the static coefficient to a larger dynamic one and copy the
210  * existing data. If successful, the value of 'result' is unchanged.
211  * Otherwise, set 'result' to NaN and update 'status' with MPD_Malloc_error.
212  */
213 int
mpd_switch_to_dyn(mpd_t * result,mpd_ssize_t nwords,uint32_t * status)214 mpd_switch_to_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
215 {
216     mpd_uint_t *p = result->data;
217 
218     assert(nwords >= result->alloc);
219 
220     result->data = mpd_alloc(nwords, sizeof *result->data);
221     if (result->data == NULL) {
222         result->data = p;
223         mpd_set_qnan(result);
224         mpd_set_positive(result);
225         result->exp = result->digits = result->len = 0;
226         *status |= MPD_Malloc_error;
227         return 0;
228     }
229 
230     memcpy(result->data, p, result->alloc * (sizeof *result->data));
231     result->alloc = nwords;
232     mpd_set_dynamic_data(result);
233     return 1;
234 }
235 
236 /*
237  * Input: 'result' is a static mpd_t with a static coefficient.
238  *
239  * Convert the coefficient to a dynamic one that is initialized to zero. If
240  * malloc fails, set 'result' to NaN and update 'status' with MPD_Malloc_error.
241  */
242 int
mpd_switch_to_dyn_zero(mpd_t * result,mpd_ssize_t nwords,uint32_t * status)243 mpd_switch_to_dyn_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
244 {
245     mpd_uint_t *p = result->data;
246 
247     result->data = mpd_calloc(nwords, sizeof *result->data);
248     if (result->data == NULL) {
249         result->data = p;
250         mpd_set_qnan(result);
251         mpd_set_positive(result);
252         result->exp = result->digits = result->len = 0;
253         *status |= MPD_Malloc_error;
254         return 0;
255     }
256 
257     result->alloc = nwords;
258     mpd_set_dynamic_data(result);
259 
260     return 1;
261 }
262 
263 /*
264  * Input: 'result' is a static or a dynamic mpd_t with a dynamic coefficient.
265  * Resize the coefficient to length 'nwords':
266  *   Case nwords > result->alloc:
267  *     If realloc is successful:
268  *       'result' has a larger coefficient but the same value. Return 1.
269  *     Otherwise:
270  *       Set 'result' to NaN, update status with MPD_Malloc_error and return 0.
271  *   Case nwords < result->alloc:
272  *     If realloc is successful:
273  *       'result' has a smaller coefficient. result->len is undefined. Return 1.
274  *     Otherwise (unlikely):
275  *       'result' is unchanged. Reuse the now oversized coefficient. Return 1.
276  */
277 int
mpd_realloc_dyn(mpd_t * result,mpd_ssize_t nwords,uint32_t * status)278 mpd_realloc_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
279 {
280     uint8_t err = 0;
281 
282     result->data = mpd_realloc(result->data, nwords, sizeof *result->data, &err);
283     if (!err) {
284         result->alloc = nwords;
285     }
286     else if (nwords > result->alloc) {
287         mpd_set_qnan(result);
288         mpd_set_positive(result);
289         result->exp = result->digits = result->len = 0;
290         *status |= MPD_Malloc_error;
291         return 0;
292     }
293 
294     return 1;
295 }
296 
297 
298