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(). */ 47mpd_ssize_t MPD_MINALLOC = MPD_MINALLOC_MIN; 48 49/* Custom allocation and free functions */ 50void *(* mpd_mallocfunc)(size_t size) = malloc; 51void *(* mpd_reallocfunc)(void *ptr, size_t size) = realloc; 52void *(* mpd_callocfunc)(size_t nmemb, size_t size) = calloc; 53void (* mpd_free)(void *ptr) = free; 54 55 56/* emulate calloc if it is not available */ 57void * 58mpd_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 */ 82void * 83mpd_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 */ 96void * 97mpd_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 */ 110void * 111mpd_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 */ 132void * 133mpd_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. */ 153mpd_t * 154mpd_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. */ 182mpd_t * 183mpd_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. */ 190mpd_t * 191mpd_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 */ 210int 211mpd_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 */ 239int 240mpd_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 */ 274int 275mpd_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 */ 303int 304mpd_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 */ 334int 335mpd_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