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