Home | History | Annotate | Download | only in libmpdec
      1 /*
      2  * Copyright (c) 2008-2016 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 #include <stdio.h>
     31 #include <stdlib.h>
     32 #include "typearith.h"
     33 #include "mpalloc.h"
     34 
     35 
     36 #if defined(_MSC_VER)
     37   #pragma warning(disable : 4232)
     38 #endif
     39 
     40 
     41 /* Guaranteed minimum allocation for a coefficient. May be changed once
     42    at program start using mpd_setminalloc(). */
     43 mpd_ssize_t MPD_MINALLOC = MPD_MINALLOC_MIN;
     44 
     45 /* Custom allocation and free functions */
     46 void *(* mpd_mallocfunc)(size_t size) = malloc;
     47 void *(* mpd_reallocfunc)(void *ptr, size_t size) = realloc;
     48 void *(* mpd_callocfunc)(size_t nmemb, size_t size) = calloc;
     49 void (* mpd_free)(void *ptr) = free;
     50 
     51 
     52 /* emulate calloc if it is not available */
     53 void *
     54 mpd_callocfunc_em(size_t nmemb, size_t size)
     55 {
     56     void *ptr;
     57     size_t req;
     58     mpd_size_t overflow;
     59 
     60 #if MPD_SIZE_MAX < SIZE_MAX
     61     /* full_coverage test only */
     62     if (nmemb > MPD_SIZE_MAX || size > MPD_SIZE_MAX) {
     63         return NULL;
     64     }
     65 #endif
     66 
     67     req = mul_size_t_overflow((mpd_size_t)nmemb, (mpd_size_t)size,
     68                               &overflow);
     69     if (overflow) {
     70         return NULL;
     71     }
     72 
     73     ptr = mpd_mallocfunc(req);
     74     if (ptr == NULL) {
     75         return NULL;
     76     }
     77     /* used on uint32_t or uint64_t */
     78     memset(ptr, 0, req);
     79 
     80     return ptr;
     81 }
     82 
     83 
     84 /* malloc with overflow checking */
     85 void *
     86 mpd_alloc(mpd_size_t nmemb, mpd_size_t size)
     87 {
     88     mpd_size_t req, overflow;
     89 
     90     req = mul_size_t_overflow(nmemb, size, &overflow);
     91     if (overflow) {
     92         return NULL;
     93     }
     94 
     95     return mpd_mallocfunc(req);
     96 }
     97 
     98 /* calloc with overflow checking */
     99 void *
    100 mpd_calloc(mpd_size_t nmemb, mpd_size_t size)
    101 {
    102     mpd_size_t overflow;
    103 
    104     (void)mul_size_t_overflow(nmemb, size, &overflow);
    105     if (overflow) {
    106         return NULL;
    107     }
    108 
    109     return mpd_callocfunc(nmemb, size);
    110 }
    111 
    112 /* realloc with overflow checking */
    113 void *
    114 mpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err)
    115 {
    116     void *new;
    117     mpd_size_t req, overflow;
    118 
    119     req = mul_size_t_overflow(nmemb, size, &overflow);
    120     if (overflow) {
    121         *err = 1;
    122         return ptr;
    123     }
    124 
    125     new = mpd_reallocfunc(ptr, req);
    126     if (new == NULL) {
    127         *err = 1;
    128         return ptr;
    129     }
    130 
    131     return new;
    132 }
    133 
    134 /* struct hack malloc with overflow checking */
    135 void *
    136 mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size)
    137 {
    138     mpd_size_t req, overflow;
    139 
    140     req = mul_size_t_overflow(nmemb, size, &overflow);
    141     if (overflow) {
    142         return NULL;
    143     }
    144 
    145     req = add_size_t_overflow(req, struct_size, &overflow);
    146     if (overflow) {
    147         return NULL;
    148     }
    149 
    150     return mpd_mallocfunc(req);
    151 }
    152 
    153 
    154 /* Allocate a new decimal with a coefficient of length 'nwords'. In case
    155    of an error the return value is NULL. */
    156 mpd_t *
    157 mpd_qnew_size(mpd_ssize_t nwords)
    158 {
    159     mpd_t *result;
    160 
    161     nwords = (nwords < MPD_MINALLOC) ? MPD_MINALLOC : nwords;
    162 
    163     result = mpd_alloc(1, sizeof *result);
    164     if (result == NULL) {
    165         return NULL;
    166     }
    167 
    168     result->data = mpd_alloc(nwords, sizeof *result->data);
    169     if (result->data == NULL) {
    170         mpd_free(result);
    171         return NULL;
    172     }
    173 
    174     result->flags = 0;
    175     result->exp = 0;
    176     result->digits = 0;
    177     result->len = 0;
    178     result->alloc = nwords;
    179 
    180     return result;
    181 }
    182 
    183 /* Allocate a new decimal with a coefficient of length MPD_MINALLOC.
    184    In case of an error the return value is NULL. */
    185 mpd_t *
    186 mpd_qnew(void)
    187 {
    188     return mpd_qnew_size(MPD_MINALLOC);
    189 }
    190 
    191 /* Allocate new decimal. Caller can check for NULL or MPD_Malloc_error.
    192    Raises on error. */
    193 mpd_t *
    194 mpd_new(mpd_context_t *ctx)
    195 {
    196     mpd_t *result;
    197 
    198     result = mpd_qnew();
    199     if (result == NULL) {
    200         mpd_addstatus_raise(ctx, MPD_Malloc_error);
    201     }
    202     return result;
    203 }
    204 
    205 /*
    206  * Input: 'result' is a static mpd_t with a static coefficient.
    207  * Assumption: 'nwords' >= result->alloc.
    208  *
    209  * Resize the static coefficient to a larger dynamic one and copy the
    210  * existing data. If successful, the value of 'result' is unchanged.
    211  * Otherwise, set 'result' to NaN and update 'status' with MPD_Malloc_error.
    212  */
    213 int
    214 mpd_switch_to_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
    215 {
    216     mpd_uint_t *p = result->data;
    217 
    218     assert(nwords >= result->alloc);
    219 
    220     result->data = mpd_alloc(nwords, sizeof *result->data);
    221     if (result->data == NULL) {
    222         result->data = p;
    223         mpd_set_qnan(result);
    224         mpd_set_positive(result);
    225         result->exp = result->digits = result->len = 0;
    226         *status |= MPD_Malloc_error;
    227         return 0;
    228     }
    229 
    230     memcpy(result->data, p, result->alloc * (sizeof *result->data));
    231     result->alloc = nwords;
    232     mpd_set_dynamic_data(result);
    233     return 1;
    234 }
    235 
    236 /*
    237  * Input: 'result' is a static mpd_t with a static coefficient.
    238  *
    239  * Convert the coefficient to a dynamic one that is initialized to zero. If
    240  * malloc fails, set 'result' to NaN and update 'status' with MPD_Malloc_error.
    241  */
    242 int
    243 mpd_switch_to_dyn_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
    244 {
    245     mpd_uint_t *p = result->data;
    246 
    247     result->data = mpd_calloc(nwords, sizeof *result->data);
    248     if (result->data == NULL) {
    249         result->data = p;
    250         mpd_set_qnan(result);
    251         mpd_set_positive(result);
    252         result->exp = result->digits = result->len = 0;
    253         *status |= MPD_Malloc_error;
    254         return 0;
    255     }
    256 
    257     result->alloc = nwords;
    258     mpd_set_dynamic_data(result);
    259 
    260     return 1;
    261 }
    262 
    263 /*
    264  * Input: 'result' is a static or a dynamic mpd_t with a dynamic coefficient.
    265  * Resize the coefficient to length 'nwords':
    266  *   Case nwords > result->alloc:
    267  *     If realloc is successful:
    268  *       'result' has a larger coefficient but the same value. Return 1.
    269  *     Otherwise:
    270  *       Set 'result' to NaN, update status with MPD_Malloc_error and return 0.
    271  *   Case nwords < result->alloc:
    272  *     If realloc is successful:
    273  *       'result' has a smaller coefficient. result->len is undefined. Return 1.
    274  *     Otherwise (unlikely):
    275  *       'result' is unchanged. Reuse the now oversized coefficient. Return 1.
    276  */
    277 int
    278 mpd_realloc_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status)
    279 {
    280     uint8_t err = 0;
    281 
    282     result->data = mpd_realloc(result->data, nwords, sizeof *result->data, &err);
    283     if (!err) {
    284         result->alloc = nwords;
    285     }
    286     else if (nwords > result->alloc) {
    287         mpd_set_qnan(result);
    288         mpd_set_positive(result);
    289         result->exp = result->digits = result->len = 0;
    290         *status |= MPD_Malloc_error;
    291         return 0;
    292     }
    293 
    294     return 1;
    295 }
    296 
    297 
    298