/*1* Copyright (c) 2008-2020 Stefan Krah. All rights reserved.2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6*7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9*10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*/262728#include "mpdecimal.h"2930#include <assert.h>31#include <stdio.h>32#include <stdlib.h>33#include <string.h>3435#include "mpalloc.h"36#include "typearith.h"373839#if defined(_MSC_VER)40#pragma warning(disable : 4232)41#endif424344/* Guaranteed minimum allocation for a coefficient. May be changed once45at program start using mpd_setminalloc(). */46mpd_ssize_t MPD_MINALLOC = MPD_MINALLOC_MIN;4748/* Custom allocation and free functions */49void *(* mpd_mallocfunc)(size_t size) = malloc;50void *(* mpd_reallocfunc)(void *ptr, size_t size) = realloc;51void *(* mpd_callocfunc)(size_t nmemb, size_t size) = calloc;52void (* mpd_free)(void *ptr) = free;535455/* emulate calloc if it is not available */56void *57mpd_callocfunc_em(size_t nmemb, size_t size)58{59void *ptr;60size_t req;61mpd_size_t overflow;6263req = mul_size_t_overflow((mpd_size_t)nmemb, (mpd_size_t)size,64&overflow);65if (overflow) {66return NULL;67}6869ptr = mpd_mallocfunc(req);70if (ptr == NULL) {71return NULL;72}73/* used on uint32_t or uint64_t */74memset(ptr, 0, req);7576return ptr;77}787980/* malloc with overflow checking */81void *82mpd_alloc(mpd_size_t nmemb, mpd_size_t size)83{84mpd_size_t req, overflow;8586req = mul_size_t_overflow(nmemb, size, &overflow);87if (overflow) {88return NULL;89}9091return mpd_mallocfunc(req);92}9394/* calloc with overflow checking */95void *96mpd_calloc(mpd_size_t nmemb, mpd_size_t size)97{98mpd_size_t overflow;99100(void)mul_size_t_overflow(nmemb, size, &overflow);101if (overflow) {102return NULL;103}104105return mpd_callocfunc(nmemb, size);106}107108/* realloc with overflow checking */109void *110mpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err)111{112void *new;113mpd_size_t req, overflow;114115req = mul_size_t_overflow(nmemb, size, &overflow);116if (overflow) {117*err = 1;118return ptr;119}120121new = mpd_reallocfunc(ptr, req);122if (new == NULL) {123*err = 1;124return ptr;125}126127return new;128}129130/* struct hack malloc with overflow checking */131void *132mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size)133{134mpd_size_t req, overflow;135136req = mul_size_t_overflow(nmemb, size, &overflow);137if (overflow) {138return NULL;139}140141req = add_size_t_overflow(req, struct_size, &overflow);142if (overflow) {143return NULL;144}145146return mpd_mallocfunc(req);147}148149150/* Allocate a new decimal with a coefficient of length 'nwords'. In case151of an error the return value is NULL. */152mpd_t *153mpd_qnew_size(mpd_ssize_t nwords)154{155mpd_t *result;156157nwords = (nwords < MPD_MINALLOC) ? MPD_MINALLOC : nwords;158159result = mpd_alloc(1, sizeof *result);160if (result == NULL) {161return NULL;162}163164result->data = mpd_alloc(nwords, sizeof *result->data);165if (result->data == NULL) {166mpd_free(result);167return NULL;168}169170result->flags = 0;171result->exp = 0;172result->digits = 0;173result->len = 0;174result->alloc = nwords;175176return result;177}178179/* Allocate a new decimal with a coefficient of length MPD_MINALLOC.180In case of an error the return value is NULL. */181mpd_t *182mpd_qnew(void)183{184return mpd_qnew_size(MPD_MINALLOC);185}186187/* Allocate new decimal. Caller can check for NULL or MPD_Malloc_error.188Raises on error. */189mpd_t *190mpd_new(mpd_context_t *ctx)191{192mpd_t *result;193194result = mpd_qnew();195if (result == NULL) {196mpd_addstatus_raise(ctx, MPD_Malloc_error);197}198return result;199}200201/*202* Input: 'result' is a static mpd_t with a static coefficient.203* Assumption: 'nwords' >= result->alloc.204*205* Resize the static coefficient to a larger dynamic one and copy the206* existing data. If successful, the value of 'result' is unchanged.207* Otherwise, set 'result' to NaN and update 'status' with MPD_Malloc_error.208*/209int210mpd_switch_to_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)211{212mpd_uint_t *p = result->data;213214assert(nwords >= result->alloc);215216result->data = mpd_alloc(nwords, sizeof *result->data);217if (result->data == NULL) {218result->data = p;219mpd_set_qnan(result);220mpd_set_positive(result);221result->exp = result->digits = result->len = 0;222*status |= MPD_Malloc_error;223return 0;224}225226memcpy(result->data, p, result->alloc * (sizeof *result->data));227result->alloc = nwords;228mpd_set_dynamic_data(result);229return 1;230}231232/*233* Input: 'result' is a static mpd_t with a static coefficient.234*235* Convert the coefficient to a dynamic one that is initialized to zero. If236* malloc fails, set 'result' to NaN and update 'status' with MPD_Malloc_error.237*/238int239mpd_switch_to_dyn_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)240{241mpd_uint_t *p = result->data;242243result->data = mpd_calloc(nwords, sizeof *result->data);244if (result->data == NULL) {245result->data = p;246mpd_set_qnan(result);247mpd_set_positive(result);248result->exp = result->digits = result->len = 0;249*status |= MPD_Malloc_error;250return 0;251}252253result->alloc = nwords;254mpd_set_dynamic_data(result);255256return 1;257}258259/*260* Input: 'result' is a static or a dynamic mpd_t with a dynamic coefficient.261* Resize the coefficient to length 'nwords':262* Case nwords > result->alloc:263* If realloc is successful:264* 'result' has a larger coefficient but the same value. Return 1.265* Otherwise:266* Set 'result' to NaN, update status with MPD_Malloc_error and return 0.267* Case nwords < result->alloc:268* If realloc is successful:269* 'result' has a smaller coefficient. result->len is undefined. Return 1.270* Otherwise (unlikely):271* 'result' is unchanged. Reuse the now oversized coefficient. Return 1.272*/273int274mpd_realloc_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)275{276uint8_t err = 0;277278result->data = mpd_realloc(result->data, nwords, sizeof *result->data, &err);279if (!err) {280result->alloc = nwords;281}282else if (nwords > result->alloc) {283mpd_set_qnan(result);284mpd_set_positive(result);285result->exp = result->digits = result->len = 0;286*status |= MPD_Malloc_error;287return 0;288}289290return 1;291}292293/*294* Input: 'result' is a static mpd_t with a static coefficient.295* Assumption: 'nwords' >= result->alloc.296*297* Resize the static coefficient to a larger dynamic one and copy the298* existing data.299*300* On failure the value of 'result' is unchanged.301*/302int303mpd_switch_to_dyn_cxx(mpd_t *result, mpd_ssize_t nwords)304{305assert(nwords >= result->alloc);306307mpd_uint_t *data = mpd_alloc(nwords, sizeof *result->data);308if (data == NULL) {309return 0;310}311312memcpy(data, result->data, result->alloc * (sizeof *result->data));313result->data = data;314result->alloc = nwords;315mpd_set_dynamic_data(result);316return 1;317}318319/*320* Input: 'result' is a static or a dynamic mpd_t with a dynamic coefficient.321* Resize the coefficient to length 'nwords':322* Case nwords > result->alloc:323* If realloc is successful:324* 'result' has a larger coefficient but the same value. Return 1.325* Otherwise:326* 'result' has a the same coefficient. Return 0.327* Case nwords < result->alloc:328* If realloc is successful:329* 'result' has a smaller coefficient. result->len is undefined. Return 1.330* Otherwise (unlikely):331* 'result' is unchanged. Reuse the now oversized coefficient. Return 1.332*/333int334mpd_realloc_dyn_cxx(mpd_t *result, mpd_ssize_t nwords)335{336uint8_t err = 0;337338mpd_uint_t *p = mpd_realloc(result->data, nwords, sizeof *result->data, &err);339if (!err) {340result->data = p;341result->alloc = nwords;342}343else if (nwords > result->alloc) {344return 0;345}346347return 1;348}349350351