17db96d56Sopenharmony_ci/* 27db96d56Sopenharmony_ci * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. 37db96d56Sopenharmony_ci * 47db96d56Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 57db96d56Sopenharmony_ci * modification, are permitted provided that the following conditions 67db96d56Sopenharmony_ci * are met: 77db96d56Sopenharmony_ci * 87db96d56Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 97db96d56Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 107db96d56Sopenharmony_ci * 117db96d56Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 127db96d56Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 137db96d56Sopenharmony_ci * documentation and/or other materials provided with the distribution. 147db96d56Sopenharmony_ci * 157db96d56Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND 167db96d56Sopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 177db96d56Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 187db96d56Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 197db96d56Sopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 207db96d56Sopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 217db96d56Sopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 227db96d56Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 237db96d56Sopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 247db96d56Sopenharmony_ci * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 257db96d56Sopenharmony_ci * SUCH DAMAGE. 267db96d56Sopenharmony_ci */ 277db96d56Sopenharmony_ci 287db96d56Sopenharmony_ci 297db96d56Sopenharmony_ci#include "mpdecimal.h" 307db96d56Sopenharmony_ci 317db96d56Sopenharmony_ci#include <assert.h> 327db96d56Sopenharmony_ci#include <stdio.h> 337db96d56Sopenharmony_ci#include <stdlib.h> 347db96d56Sopenharmony_ci#include <string.h> 357db96d56Sopenharmony_ci 367db96d56Sopenharmony_ci#include "mpalloc.h" 377db96d56Sopenharmony_ci#include "typearith.h" 387db96d56Sopenharmony_ci 397db96d56Sopenharmony_ci 407db96d56Sopenharmony_ci#if defined(_MSC_VER) 417db96d56Sopenharmony_ci #pragma warning(disable : 4232) 427db96d56Sopenharmony_ci#endif 437db96d56Sopenharmony_ci 447db96d56Sopenharmony_ci 457db96d56Sopenharmony_ci/* Guaranteed minimum allocation for a coefficient. May be changed once 467db96d56Sopenharmony_ci at program start using mpd_setminalloc(). */ 477db96d56Sopenharmony_cimpd_ssize_t MPD_MINALLOC = MPD_MINALLOC_MIN; 487db96d56Sopenharmony_ci 497db96d56Sopenharmony_ci/* Custom allocation and free functions */ 507db96d56Sopenharmony_civoid *(* mpd_mallocfunc)(size_t size) = malloc; 517db96d56Sopenharmony_civoid *(* mpd_reallocfunc)(void *ptr, size_t size) = realloc; 527db96d56Sopenharmony_civoid *(* mpd_callocfunc)(size_t nmemb, size_t size) = calloc; 537db96d56Sopenharmony_civoid (* mpd_free)(void *ptr) = free; 547db96d56Sopenharmony_ci 557db96d56Sopenharmony_ci 567db96d56Sopenharmony_ci/* emulate calloc if it is not available */ 577db96d56Sopenharmony_civoid * 587db96d56Sopenharmony_cimpd_callocfunc_em(size_t nmemb, size_t size) 597db96d56Sopenharmony_ci{ 607db96d56Sopenharmony_ci void *ptr; 617db96d56Sopenharmony_ci size_t req; 627db96d56Sopenharmony_ci mpd_size_t overflow; 637db96d56Sopenharmony_ci 647db96d56Sopenharmony_ci req = mul_size_t_overflow((mpd_size_t)nmemb, (mpd_size_t)size, 657db96d56Sopenharmony_ci &overflow); 667db96d56Sopenharmony_ci if (overflow) { 677db96d56Sopenharmony_ci return NULL; 687db96d56Sopenharmony_ci } 697db96d56Sopenharmony_ci 707db96d56Sopenharmony_ci ptr = mpd_mallocfunc(req); 717db96d56Sopenharmony_ci if (ptr == NULL) { 727db96d56Sopenharmony_ci return NULL; 737db96d56Sopenharmony_ci } 747db96d56Sopenharmony_ci /* used on uint32_t or uint64_t */ 757db96d56Sopenharmony_ci memset(ptr, 0, req); 767db96d56Sopenharmony_ci 777db96d56Sopenharmony_ci return ptr; 787db96d56Sopenharmony_ci} 797db96d56Sopenharmony_ci 807db96d56Sopenharmony_ci 817db96d56Sopenharmony_ci/* malloc with overflow checking */ 827db96d56Sopenharmony_civoid * 837db96d56Sopenharmony_cimpd_alloc(mpd_size_t nmemb, mpd_size_t size) 847db96d56Sopenharmony_ci{ 857db96d56Sopenharmony_ci mpd_size_t req, overflow; 867db96d56Sopenharmony_ci 877db96d56Sopenharmony_ci req = mul_size_t_overflow(nmemb, size, &overflow); 887db96d56Sopenharmony_ci if (overflow) { 897db96d56Sopenharmony_ci return NULL; 907db96d56Sopenharmony_ci } 917db96d56Sopenharmony_ci 927db96d56Sopenharmony_ci return mpd_mallocfunc(req); 937db96d56Sopenharmony_ci} 947db96d56Sopenharmony_ci 957db96d56Sopenharmony_ci/* calloc with overflow checking */ 967db96d56Sopenharmony_civoid * 977db96d56Sopenharmony_cimpd_calloc(mpd_size_t nmemb, mpd_size_t size) 987db96d56Sopenharmony_ci{ 997db96d56Sopenharmony_ci mpd_size_t overflow; 1007db96d56Sopenharmony_ci 1017db96d56Sopenharmony_ci (void)mul_size_t_overflow(nmemb, size, &overflow); 1027db96d56Sopenharmony_ci if (overflow) { 1037db96d56Sopenharmony_ci return NULL; 1047db96d56Sopenharmony_ci } 1057db96d56Sopenharmony_ci 1067db96d56Sopenharmony_ci return mpd_callocfunc(nmemb, size); 1077db96d56Sopenharmony_ci} 1087db96d56Sopenharmony_ci 1097db96d56Sopenharmony_ci/* realloc with overflow checking */ 1107db96d56Sopenharmony_civoid * 1117db96d56Sopenharmony_cimpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err) 1127db96d56Sopenharmony_ci{ 1137db96d56Sopenharmony_ci void *new; 1147db96d56Sopenharmony_ci mpd_size_t req, overflow; 1157db96d56Sopenharmony_ci 1167db96d56Sopenharmony_ci req = mul_size_t_overflow(nmemb, size, &overflow); 1177db96d56Sopenharmony_ci if (overflow) { 1187db96d56Sopenharmony_ci *err = 1; 1197db96d56Sopenharmony_ci return ptr; 1207db96d56Sopenharmony_ci } 1217db96d56Sopenharmony_ci 1227db96d56Sopenharmony_ci new = mpd_reallocfunc(ptr, req); 1237db96d56Sopenharmony_ci if (new == NULL) { 1247db96d56Sopenharmony_ci *err = 1; 1257db96d56Sopenharmony_ci return ptr; 1267db96d56Sopenharmony_ci } 1277db96d56Sopenharmony_ci 1287db96d56Sopenharmony_ci return new; 1297db96d56Sopenharmony_ci} 1307db96d56Sopenharmony_ci 1317db96d56Sopenharmony_ci/* struct hack malloc with overflow checking */ 1327db96d56Sopenharmony_civoid * 1337db96d56Sopenharmony_cimpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size) 1347db96d56Sopenharmony_ci{ 1357db96d56Sopenharmony_ci mpd_size_t req, overflow; 1367db96d56Sopenharmony_ci 1377db96d56Sopenharmony_ci req = mul_size_t_overflow(nmemb, size, &overflow); 1387db96d56Sopenharmony_ci if (overflow) { 1397db96d56Sopenharmony_ci return NULL; 1407db96d56Sopenharmony_ci } 1417db96d56Sopenharmony_ci 1427db96d56Sopenharmony_ci req = add_size_t_overflow(req, struct_size, &overflow); 1437db96d56Sopenharmony_ci if (overflow) { 1447db96d56Sopenharmony_ci return NULL; 1457db96d56Sopenharmony_ci } 1467db96d56Sopenharmony_ci 1477db96d56Sopenharmony_ci return mpd_mallocfunc(req); 1487db96d56Sopenharmony_ci} 1497db96d56Sopenharmony_ci 1507db96d56Sopenharmony_ci 1517db96d56Sopenharmony_ci/* Allocate a new decimal with a coefficient of length 'nwords'. In case 1527db96d56Sopenharmony_ci of an error the return value is NULL. */ 1537db96d56Sopenharmony_cimpd_t * 1547db96d56Sopenharmony_cimpd_qnew_size(mpd_ssize_t nwords) 1557db96d56Sopenharmony_ci{ 1567db96d56Sopenharmony_ci mpd_t *result; 1577db96d56Sopenharmony_ci 1587db96d56Sopenharmony_ci nwords = (nwords < MPD_MINALLOC) ? MPD_MINALLOC : nwords; 1597db96d56Sopenharmony_ci 1607db96d56Sopenharmony_ci result = mpd_alloc(1, sizeof *result); 1617db96d56Sopenharmony_ci if (result == NULL) { 1627db96d56Sopenharmony_ci return NULL; 1637db96d56Sopenharmony_ci } 1647db96d56Sopenharmony_ci 1657db96d56Sopenharmony_ci result->data = mpd_alloc(nwords, sizeof *result->data); 1667db96d56Sopenharmony_ci if (result->data == NULL) { 1677db96d56Sopenharmony_ci mpd_free(result); 1687db96d56Sopenharmony_ci return NULL; 1697db96d56Sopenharmony_ci } 1707db96d56Sopenharmony_ci 1717db96d56Sopenharmony_ci result->flags = 0; 1727db96d56Sopenharmony_ci result->exp = 0; 1737db96d56Sopenharmony_ci result->digits = 0; 1747db96d56Sopenharmony_ci result->len = 0; 1757db96d56Sopenharmony_ci result->alloc = nwords; 1767db96d56Sopenharmony_ci 1777db96d56Sopenharmony_ci return result; 1787db96d56Sopenharmony_ci} 1797db96d56Sopenharmony_ci 1807db96d56Sopenharmony_ci/* Allocate a new decimal with a coefficient of length MPD_MINALLOC. 1817db96d56Sopenharmony_ci In case of an error the return value is NULL. */ 1827db96d56Sopenharmony_cimpd_t * 1837db96d56Sopenharmony_cimpd_qnew(void) 1847db96d56Sopenharmony_ci{ 1857db96d56Sopenharmony_ci return mpd_qnew_size(MPD_MINALLOC); 1867db96d56Sopenharmony_ci} 1877db96d56Sopenharmony_ci 1887db96d56Sopenharmony_ci/* Allocate new decimal. Caller can check for NULL or MPD_Malloc_error. 1897db96d56Sopenharmony_ci Raises on error. */ 1907db96d56Sopenharmony_cimpd_t * 1917db96d56Sopenharmony_cimpd_new(mpd_context_t *ctx) 1927db96d56Sopenharmony_ci{ 1937db96d56Sopenharmony_ci mpd_t *result; 1947db96d56Sopenharmony_ci 1957db96d56Sopenharmony_ci result = mpd_qnew(); 1967db96d56Sopenharmony_ci if (result == NULL) { 1977db96d56Sopenharmony_ci mpd_addstatus_raise(ctx, MPD_Malloc_error); 1987db96d56Sopenharmony_ci } 1997db96d56Sopenharmony_ci return result; 2007db96d56Sopenharmony_ci} 2017db96d56Sopenharmony_ci 2027db96d56Sopenharmony_ci/* 2037db96d56Sopenharmony_ci * Input: 'result' is a static mpd_t with a static coefficient. 2047db96d56Sopenharmony_ci * Assumption: 'nwords' >= result->alloc. 2057db96d56Sopenharmony_ci * 2067db96d56Sopenharmony_ci * Resize the static coefficient to a larger dynamic one and copy the 2077db96d56Sopenharmony_ci * existing data. If successful, the value of 'result' is unchanged. 2087db96d56Sopenharmony_ci * Otherwise, set 'result' to NaN and update 'status' with MPD_Malloc_error. 2097db96d56Sopenharmony_ci */ 2107db96d56Sopenharmony_ciint 2117db96d56Sopenharmony_cimpd_switch_to_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) 2127db96d56Sopenharmony_ci{ 2137db96d56Sopenharmony_ci mpd_uint_t *p = result->data; 2147db96d56Sopenharmony_ci 2157db96d56Sopenharmony_ci assert(nwords >= result->alloc); 2167db96d56Sopenharmony_ci 2177db96d56Sopenharmony_ci result->data = mpd_alloc(nwords, sizeof *result->data); 2187db96d56Sopenharmony_ci if (result->data == NULL) { 2197db96d56Sopenharmony_ci result->data = p; 2207db96d56Sopenharmony_ci mpd_set_qnan(result); 2217db96d56Sopenharmony_ci mpd_set_positive(result); 2227db96d56Sopenharmony_ci result->exp = result->digits = result->len = 0; 2237db96d56Sopenharmony_ci *status |= MPD_Malloc_error; 2247db96d56Sopenharmony_ci return 0; 2257db96d56Sopenharmony_ci } 2267db96d56Sopenharmony_ci 2277db96d56Sopenharmony_ci memcpy(result->data, p, result->alloc * (sizeof *result->data)); 2287db96d56Sopenharmony_ci result->alloc = nwords; 2297db96d56Sopenharmony_ci mpd_set_dynamic_data(result); 2307db96d56Sopenharmony_ci return 1; 2317db96d56Sopenharmony_ci} 2327db96d56Sopenharmony_ci 2337db96d56Sopenharmony_ci/* 2347db96d56Sopenharmony_ci * Input: 'result' is a static mpd_t with a static coefficient. 2357db96d56Sopenharmony_ci * 2367db96d56Sopenharmony_ci * Convert the coefficient to a dynamic one that is initialized to zero. If 2377db96d56Sopenharmony_ci * malloc fails, set 'result' to NaN and update 'status' with MPD_Malloc_error. 2387db96d56Sopenharmony_ci */ 2397db96d56Sopenharmony_ciint 2407db96d56Sopenharmony_cimpd_switch_to_dyn_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) 2417db96d56Sopenharmony_ci{ 2427db96d56Sopenharmony_ci mpd_uint_t *p = result->data; 2437db96d56Sopenharmony_ci 2447db96d56Sopenharmony_ci result->data = mpd_calloc(nwords, sizeof *result->data); 2457db96d56Sopenharmony_ci if (result->data == NULL) { 2467db96d56Sopenharmony_ci result->data = p; 2477db96d56Sopenharmony_ci mpd_set_qnan(result); 2487db96d56Sopenharmony_ci mpd_set_positive(result); 2497db96d56Sopenharmony_ci result->exp = result->digits = result->len = 0; 2507db96d56Sopenharmony_ci *status |= MPD_Malloc_error; 2517db96d56Sopenharmony_ci return 0; 2527db96d56Sopenharmony_ci } 2537db96d56Sopenharmony_ci 2547db96d56Sopenharmony_ci result->alloc = nwords; 2557db96d56Sopenharmony_ci mpd_set_dynamic_data(result); 2567db96d56Sopenharmony_ci 2577db96d56Sopenharmony_ci return 1; 2587db96d56Sopenharmony_ci} 2597db96d56Sopenharmony_ci 2607db96d56Sopenharmony_ci/* 2617db96d56Sopenharmony_ci * Input: 'result' is a static or a dynamic mpd_t with a dynamic coefficient. 2627db96d56Sopenharmony_ci * Resize the coefficient to length 'nwords': 2637db96d56Sopenharmony_ci * Case nwords > result->alloc: 2647db96d56Sopenharmony_ci * If realloc is successful: 2657db96d56Sopenharmony_ci * 'result' has a larger coefficient but the same value. Return 1. 2667db96d56Sopenharmony_ci * Otherwise: 2677db96d56Sopenharmony_ci * Set 'result' to NaN, update status with MPD_Malloc_error and return 0. 2687db96d56Sopenharmony_ci * Case nwords < result->alloc: 2697db96d56Sopenharmony_ci * If realloc is successful: 2707db96d56Sopenharmony_ci * 'result' has a smaller coefficient. result->len is undefined. Return 1. 2717db96d56Sopenharmony_ci * Otherwise (unlikely): 2727db96d56Sopenharmony_ci * 'result' is unchanged. Reuse the now oversized coefficient. Return 1. 2737db96d56Sopenharmony_ci */ 2747db96d56Sopenharmony_ciint 2757db96d56Sopenharmony_cimpd_realloc_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) 2767db96d56Sopenharmony_ci{ 2777db96d56Sopenharmony_ci uint8_t err = 0; 2787db96d56Sopenharmony_ci 2797db96d56Sopenharmony_ci result->data = mpd_realloc(result->data, nwords, sizeof *result->data, &err); 2807db96d56Sopenharmony_ci if (!err) { 2817db96d56Sopenharmony_ci result->alloc = nwords; 2827db96d56Sopenharmony_ci } 2837db96d56Sopenharmony_ci else if (nwords > result->alloc) { 2847db96d56Sopenharmony_ci mpd_set_qnan(result); 2857db96d56Sopenharmony_ci mpd_set_positive(result); 2867db96d56Sopenharmony_ci result->exp = result->digits = result->len = 0; 2877db96d56Sopenharmony_ci *status |= MPD_Malloc_error; 2887db96d56Sopenharmony_ci return 0; 2897db96d56Sopenharmony_ci } 2907db96d56Sopenharmony_ci 2917db96d56Sopenharmony_ci return 1; 2927db96d56Sopenharmony_ci} 2937db96d56Sopenharmony_ci 2947db96d56Sopenharmony_ci/* 2957db96d56Sopenharmony_ci * Input: 'result' is a static mpd_t with a static coefficient. 2967db96d56Sopenharmony_ci * Assumption: 'nwords' >= result->alloc. 2977db96d56Sopenharmony_ci * 2987db96d56Sopenharmony_ci * Resize the static coefficient to a larger dynamic one and copy the 2997db96d56Sopenharmony_ci * existing data. 3007db96d56Sopenharmony_ci * 3017db96d56Sopenharmony_ci * On failure the value of 'result' is unchanged. 3027db96d56Sopenharmony_ci */ 3037db96d56Sopenharmony_ciint 3047db96d56Sopenharmony_cimpd_switch_to_dyn_cxx(mpd_t *result, mpd_ssize_t nwords) 3057db96d56Sopenharmony_ci{ 3067db96d56Sopenharmony_ci assert(nwords >= result->alloc); 3077db96d56Sopenharmony_ci 3087db96d56Sopenharmony_ci mpd_uint_t *data = mpd_alloc(nwords, sizeof *result->data); 3097db96d56Sopenharmony_ci if (data == NULL) { 3107db96d56Sopenharmony_ci return 0; 3117db96d56Sopenharmony_ci } 3127db96d56Sopenharmony_ci 3137db96d56Sopenharmony_ci memcpy(data, result->data, result->alloc * (sizeof *result->data)); 3147db96d56Sopenharmony_ci result->data = data; 3157db96d56Sopenharmony_ci result->alloc = nwords; 3167db96d56Sopenharmony_ci mpd_set_dynamic_data(result); 3177db96d56Sopenharmony_ci return 1; 3187db96d56Sopenharmony_ci} 3197db96d56Sopenharmony_ci 3207db96d56Sopenharmony_ci/* 3217db96d56Sopenharmony_ci * Input: 'result' is a static or a dynamic mpd_t with a dynamic coefficient. 3227db96d56Sopenharmony_ci * Resize the coefficient to length 'nwords': 3237db96d56Sopenharmony_ci * Case nwords > result->alloc: 3247db96d56Sopenharmony_ci * If realloc is successful: 3257db96d56Sopenharmony_ci * 'result' has a larger coefficient but the same value. Return 1. 3267db96d56Sopenharmony_ci * Otherwise: 3277db96d56Sopenharmony_ci * 'result' has a the same coefficient. Return 0. 3287db96d56Sopenharmony_ci * Case nwords < result->alloc: 3297db96d56Sopenharmony_ci * If realloc is successful: 3307db96d56Sopenharmony_ci * 'result' has a smaller coefficient. result->len is undefined. Return 1. 3317db96d56Sopenharmony_ci * Otherwise (unlikely): 3327db96d56Sopenharmony_ci * 'result' is unchanged. Reuse the now oversized coefficient. Return 1. 3337db96d56Sopenharmony_ci */ 3347db96d56Sopenharmony_ciint 3357db96d56Sopenharmony_cimpd_realloc_dyn_cxx(mpd_t *result, mpd_ssize_t nwords) 3367db96d56Sopenharmony_ci{ 3377db96d56Sopenharmony_ci uint8_t err = 0; 3387db96d56Sopenharmony_ci 3397db96d56Sopenharmony_ci mpd_uint_t *p = mpd_realloc(result->data, nwords, sizeof *result->data, &err); 3407db96d56Sopenharmony_ci if (!err) { 3417db96d56Sopenharmony_ci result->data = p; 3427db96d56Sopenharmony_ci result->alloc = nwords; 3437db96d56Sopenharmony_ci } 3447db96d56Sopenharmony_ci else if (nwords > result->alloc) { 3457db96d56Sopenharmony_ci return 0; 3467db96d56Sopenharmony_ci } 3477db96d56Sopenharmony_ci 3487db96d56Sopenharmony_ci return 1; 3497db96d56Sopenharmony_ci} 350