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