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 * |
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 | 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 */ |
82 | void * |
83 | mpd_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 */ |
96 | void * |
97 | mpd_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 */ |
110 | void * |
111 | mpd_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 */ |
132 | void * |
133 | mpd_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. */ |
153 | mpd_t * |
154 | mpd_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. */ |
182 | mpd_t * |
183 | mpd_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. */ |
190 | mpd_t * |
191 | mpd_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 | */ |
210 | int |
211 | mpd_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 | */ |
239 | int |
240 | mpd_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 | */ |
274 | int |
275 | mpd_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 | */ |
303 | int |
304 | mpd_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 | */ |
334 | int |
335 | mpd_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 | |