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