mpalloc.c 9.0 KB

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