diff options
Diffstat (limited to 'Build/source/libs/mpfr/mpfr-src/src/fpif.c')
-rw-r--r-- | Build/source/libs/mpfr/mpfr-src/src/fpif.c | 651 |
1 files changed, 651 insertions, 0 deletions
diff --git a/Build/source/libs/mpfr/mpfr-src/src/fpif.c b/Build/source/libs/mpfr/mpfr-src/src/fpif.c new file mode 100644 index 00000000000..7dd620ba7be --- /dev/null +++ b/Build/source/libs/mpfr/mpfr-src/src/fpif.c @@ -0,0 +1,651 @@ +/* mpfr_fpif -- Binary export & import of MPFR numbers + (floating-point interchange format) + +Copyright 2012-2017 Free Software Foundation, Inc. +Contributed by Olivier Demengeon. + +This file is part of the GNU MPFR Library. + +The GNU MPFR Library is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +The GNU MPFR Library is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the GNU MPFR Library; see the file COPYING.LESSER. If not, see +http://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include "mpfr-impl.h" + +#if !defined (HAVE_BIG_ENDIAN) && !defined (HAVE_LITTLE_ENDIAN) +#error "Endianness is unknown. Not supported yet." +#endif + +/* The format is described as follows. Any multi-byte number is encoded + in little endian. + + 1. We first store the precision p (this format is able to represent + any precision from 1 to 2^64 + 248). + Let B be the first byte (0 <= B <= 255). + * If B >= 8, the precision p is B-7. + Here, the condition is equivalent to 1 <= p <= 248. + * If B <= 7, the next B+1 bytes contain p-249. + Here, the condition is equivalent to 249 <= p <= 2^64 + 248. + We will use the following macros: + * MPFR_MAX_PRECSIZE = 7 + * MPFR_MAX_EMBEDDED_PRECISION = 255 - 7 = 248 + + 2. Then we store the sign bit and exponent related information + (possibly a special value). We first have byte A = [seeeeeee], + where s is the sign bit and E = [eeeeeee] such that: + * If 0 <= E <= 94, then the exponent e is E-47 (-47 <= e <= 47). + * If 95 <= E <= 110, the exponent is stored in the next E-94 bytes + (1 to 16 bytes) in sign + absolute value representation. + * If 111 <= E <= 118, the exponent size S is stored in the next + E-110 bytes (1 to 8), then the exponent itself is stored in the + next S bytes. [Not implemented yet] + * If 119 <= E <= 127, we have a special value: + E = 119 (MPFR_KIND_ZERO) for a signed zero; + E = 120 (MPFR_KIND_INF) for a signed infinity; + E = 121 (MPFR_KIND_NAN) for NaN. + + 3. Then we store the significand (for regular values). + + The sign bit is preserved by the import/export functions, even for NaN. + + Note: When a size is stored, it must be minimal, i.e. a number cannot + start with a null byte. Otherwise the import may fail. +*/ + +#define MPFR_MAX_PRECSIZE 7 +#define MPFR_MAX_EMBEDDED_PRECISION (255 - MPFR_MAX_PRECSIZE) + +#define MPFR_KIND_ZERO 119 +#define MPFR_KIND_INF 120 +#define MPFR_KIND_NAN 121 +#define MPFR_MAX_EMBEDDED_EXPONENT 47 +#define MPFR_EXTERNAL_EXPONENT 94 + +/* Begin: Low level helper functions */ + +/* storage must have an unsigned type */ +#define COUNT_NB_BYTE(storage, size) \ + do \ + { \ + (storage) >>= 8; \ + (size)++; \ + } \ + while ((storage) != 0) + +#define ALLOC_RESULT(buffer, buffer_size, wanted_size) \ + do \ + { \ + if ((buffer) == NULL || *(buffer_size) < (wanted_size)) \ + { \ + (buffer) = (unsigned char *) mpfr_reallocate_func \ + ((buffer), *(buffer_size), (wanted_size)); \ + if ((buffer) == NULL) \ + { \ + *(buffer_size) = 0; \ + return NULL; \ + } \ + } \ + *(buffer_size) = (wanted_size); \ + } \ + while (0) + +/* + * size in byte of a MPFR number in a binary object of a variable size + */ +#define MAX_VARIABLE_STORAGE(exponent_size, precision) \ + ((size_t)(((precision) >> 3) + (exponent_size) + \ + ((precision) > 248 ? sizeof(mpfr_prec_t) : 0) + 3)) + +/* copy in result[] the values in data[] with a different endianness, + where data_size might be smaller than data_max_size, so that we only + copy data_size bytes from the end of data[]. */ +static void +#if defined (HAVE_BIG_ENDIAN) +putLittleEndianData (unsigned char *result, unsigned char *data, + size_t data_max_size, size_t data_size) +#elif defined (HAVE_LITTLE_ENDIAN) +putBigEndianData (unsigned char *result, unsigned char *data, + size_t data_max_size, size_t data_size) +#endif +{ + size_t j; + + MPFR_ASSERTD (data_size <= data_max_size); + for (j = 0; j < data_size; j++) + result[j] = data[data_max_size - j - 1]; +} + +/* copy in result[] the values in data[] with the same endianness */ +static void +#if defined (HAVE_BIG_ENDIAN) +putBigEndianData (unsigned char *result, unsigned char *data, + size_t data_max_size, size_t data_size) +#elif defined (HAVE_LITTLE_ENDIAN) +putLittleEndianData (unsigned char *result, unsigned char *data, + size_t data_max_size, size_t data_size) +#endif +{ + MPFR_ASSERTD (data_size <= data_max_size); + memcpy (result, data, data_size); +} + +/* copy in result[] the values in data[] with a different endianness; + the data are written at the end of the result[] buffer (if + data_size < data_max_size, the first bytes of result[] are + left untouched). */ +static void +#if defined (HAVE_BIG_ENDIAN) +getLittleEndianData (unsigned char *result, unsigned char *data, + size_t data_max_size, size_t data_size) +#elif defined (HAVE_LITTLE_ENDIAN) +getBigEndianData (unsigned char *result, unsigned char *data, + size_t data_max_size, size_t data_size) +#endif +{ + size_t j; + + MPFR_ASSERTD (data_size <= data_max_size); + for (j = 0; j < data_size; j++) + result[data_max_size - j - 1] = data[j]; +} + +/* copy in result[] the values in data[] with the same endianness */ +static void +#if defined (HAVE_BIG_ENDIAN) +getBigEndianData (unsigned char *result, unsigned char *data, + size_t data_max_size, size_t data_size) +#elif defined (HAVE_LITTLE_ENDIAN) +getLittleEndianData (unsigned char *result, unsigned char *data, + size_t data_max_size, size_t data_size) +#endif +{ + MPFR_ASSERTD (data_size <= data_max_size); + memcpy (result, data, data_size); +} + +/* End: Low level helper functions */ + +/* Internal Function */ +/* + * buffer : OUT : store the precision in binary format, can be null + * (may be reallocated if too small) + * buffer_size : IN/OUT : size of the buffer => size used in the buffer + * precision : IN : precision to store + * return pointer to a buffer storing the precision in binary format + */ +static unsigned char * +mpfr_fpif_store_precision (unsigned char *buffer, size_t *buffer_size, + mpfr_prec_t precision) +{ + unsigned char *result; + size_t size_precision; + + MPFR_ASSERTD (precision >= 1); + size_precision = 0; + + if (precision > MPFR_MAX_EMBEDDED_PRECISION) + { + mpfr_uprec_t copy_precision; + + copy_precision = precision - (MPFR_MAX_EMBEDDED_PRECISION + 1); + COUNT_NB_BYTE(copy_precision, size_precision); + } + + result = buffer; + ALLOC_RESULT(result, buffer_size, size_precision + 1); + + if (precision > MPFR_MAX_EMBEDDED_PRECISION) + { + result[0] = size_precision - 1; + precision -= (MPFR_MAX_EMBEDDED_PRECISION + 1); + putLittleEndianData (result + 1, (unsigned char *) &precision, + sizeof(mpfr_prec_t), size_precision); + } + else + result[0] = precision + MPFR_MAX_PRECSIZE; + + return result; +} + +#define BUFFER_SIZE 8 + +/* + * fh : IN : file handler + * return the precision stored in the binary buffer, 0 in case of error + */ +static mpfr_prec_t +mpfr_fpif_read_precision_from_file (FILE *fh) +{ + mpfr_prec_t precision; + size_t precision_size; + unsigned char buffer[BUFFER_SIZE]; + + if (fh == NULL) + return 0; + + if (fread (buffer, 1, 1, fh) != 1) + return 0; + + precision_size = buffer[0]; + if (precision_size > MPFR_MAX_PRECSIZE) + return precision_size - MPFR_MAX_PRECSIZE; + + precision_size++; + MPFR_ASSERTD (precision_size <= BUFFER_SIZE); + + /* Read the precision in little-endian format. */ + if (fread (buffer, precision_size, 1, fh) != 1) + return 0; + + while (precision_size > sizeof(mpfr_prec_t)) + { + if (buffer[precision_size-1] != 0) + return 0; /* the read precision doesn't fit in a mpfr_prec_t */ + precision_size--; + } + + if (precision_size == sizeof(mpfr_prec_t) && + buffer[precision_size-1] >= 0x80) + return 0; /* the read precision doesn't fit in a mpfr_prec_t */ + + precision = 0; /* to pad with 0's if data_size < data_max_size */ + + /* On big-endian machines, the data must be copied at the end of the + precision object in the memory; thus data_max_size (3rd argument) + must be sizeof(mpfr_prec_t). */ + getLittleEndianData ((unsigned char *) &precision, buffer, + sizeof(mpfr_prec_t), precision_size); + + return precision + (MPFR_MAX_EMBEDDED_PRECISION + 1); +} + +/* + * buffer : OUT : store the kind of the MPFR number x, its sign, the size of + * its exponent and its exponent value in a binary format, + * can be null (may be reallocated if too small) + * buffer_size : IN/OUT : size of the buffer => size used in the buffer + * x : IN : MPFR number + * return pointer to a buffer storing the kind of the MPFR number x, its sign, + * the size of its exponent and its exponent value in a binary format, + */ +/* TODO + * Exponents that use more than 16 bytes are not managed (not an issue + * until one has integer types larger than 128 bits). + */ +static unsigned char* +mpfr_fpif_store_exponent (unsigned char *buffer, size_t *buffer_size, mpfr_t x) +{ + unsigned char *result; + mpfr_uexp_t uexp; + size_t exponent_size; + + exponent_size = 0; + + if (MPFR_IS_PURE_FP (x)) + { + mpfr_exp_t exponent = MPFR_GET_EXP (x); + + if (exponent > MPFR_MAX_EMBEDDED_EXPONENT || + exponent < -MPFR_MAX_EMBEDDED_EXPONENT) + { + mpfr_uexp_t copy_exponent, exp_sign_bit; + + uexp = SAFE_ABS (mpfr_uexp_t, exponent) + - MPFR_MAX_EMBEDDED_EXPONENT; + + /* Shift uexp to take the sign bit of the exponent into account. + Because of constraints on the valid exponents, this cannot + overflow (check with an MPFR_ASSERTD). */ + copy_exponent = uexp << 1; + MPFR_ASSERTD (copy_exponent > uexp); + COUNT_NB_BYTE(copy_exponent, exponent_size); + MPFR_ASSERTN (exponent_size <= 16); /* see TODO */ + + /* Sign bit of the exponent. */ + exp_sign_bit = (mpfr_uexp_t) 1 << (8 * exponent_size - 1); + MPFR_ASSERTD (uexp < exp_sign_bit); + if (exponent < 0) + uexp |= exp_sign_bit; + } + else + uexp = exponent + MPFR_MAX_EMBEDDED_EXPONENT; + } + + result = buffer; + ALLOC_RESULT(result, buffer_size, exponent_size + 1); + + if (MPFR_IS_PURE_FP (x)) + { + if (exponent_size == 0) + result[0] = uexp; + else + { + result[0] = MPFR_EXTERNAL_EXPONENT + exponent_size; + + putLittleEndianData (result + 1, (unsigned char *) &uexp, + sizeof(mpfr_exp_t), exponent_size); + } + } + else if (MPFR_IS_ZERO (x)) + result[0] = MPFR_KIND_ZERO; + else if (MPFR_IS_INF (x)) + result[0] = MPFR_KIND_INF; + else + { + MPFR_ASSERTD (MPFR_IS_NAN (x)); + result[0] = MPFR_KIND_NAN; + } + + /* Set the sign, even for NaN. */ + if (MPFR_IS_NEG (x)) + result[0] |= 0x80; + + return result; +} + +/* + * x : OUT : MPFR number extracted from the binary buffer + * fh : IN : file handler + * return 0 if successful + */ +/* TODO + * Exponents that use more than 16 bytes are not managed (this is not + * an issue if the data were written by MPFR with mpfr_exp_t not larger + * than 128 bits). + */ +static int +mpfr_fpif_read_exponent_from_file (mpfr_t x, FILE * fh) +{ + mpfr_exp_t exponent; + mpfr_uexp_t uexp; + size_t exponent_size; + int sign; + unsigned char buffer[sizeof(mpfr_exp_t)]; + + if (fh == NULL) + return 1; + + if (fread (buffer, 1, 1, fh) != 1) + return 1; + + /* sign value that can be used with MPFR_SET_SIGN, + mpfr_set_zero and mpfr_set_inf */ + sign = (buffer[0] & 0x80) ? MPFR_SIGN_NEG : MPFR_SIGN_POS; + /* Set the sign, even for NaN. */ + MPFR_SET_SIGN (x, sign); + + exponent = buffer[0] & 0x7F; + exponent_size = 1; + + if (exponent > MPFR_EXTERNAL_EXPONENT && exponent < MPFR_KIND_ZERO) + { + mpfr_uexp_t exp_sign_bit; + + exponent_size = exponent - MPFR_EXTERNAL_EXPONENT; + + /* A failure is acceptable when the exponent starts with leading zeros, + even if it would fit in mpfr_exp_t (see format description). */ + if (MPFR_UNLIKELY (exponent_size > 16 /* see TODO */ || + exponent_size > sizeof(mpfr_exp_t))) + return 1; + + if (MPFR_UNLIKELY (fread (buffer, exponent_size, 1, fh) != 1)) + return 1; + + uexp = 0; + getLittleEndianData ((unsigned char *) &uexp, buffer, + sizeof(mpfr_exp_t), exponent_size); + + /* Sign bit of the exponent. */ + exp_sign_bit = uexp & ((mpfr_uexp_t) 1 << (8 * exponent_size - 1)); + + uexp &= ~exp_sign_bit; + uexp += MPFR_MAX_EMBEDDED_EXPONENT; + if (MPFR_UNLIKELY (uexp > MPFR_EMAX_MAX && uexp > -MPFR_EMIN_MIN)) + return 1; + + exponent = exp_sign_bit ? - (mpfr_exp_t) uexp : (mpfr_exp_t) uexp; + if (MPFR_UNLIKELY (! MPFR_EXP_IN_RANGE (exponent))) + return 1; + MPFR_SET_EXP (x, exponent); + + exponent_size++; + } + else if (exponent == MPFR_KIND_ZERO) + MPFR_SET_ZERO (x); + else if (exponent == MPFR_KIND_INF) + MPFR_SET_INF (x); + else if (exponent == MPFR_KIND_NAN) + MPFR_SET_NAN (x); + else if (exponent <= MPFR_EXTERNAL_EXPONENT) + { + exponent -= MPFR_MAX_EMBEDDED_EXPONENT; + if (MPFR_UNLIKELY (! MPFR_EXP_IN_RANGE (exponent))) + return 1; + MPFR_SET_EXP (x, exponent); + } + else + return 1; + + return 0; +} + +/* + * buffer : OUT : store the limb of the MPFR number x in a binary format, + * can be null (may be reallocated if too small) + * buffer_size : IN/OUT : size of the buffer => size used in the buffer + * x : IN : MPFR number + * return pointer to a buffer storing the limb of the MPFR number x in a binary + * format + */ +static unsigned char* +mpfr_fpif_store_limbs (unsigned char *buffer, size_t *buffer_size, mpfr_t x) +{ + unsigned char *result; + mpfr_prec_t precision; + size_t nb_byte; + size_t nb_limb, mp_bytes_per_limb; + size_t nb_partial_byte; + size_t i, j; + + precision = mpfr_get_prec (x); + nb_byte = (precision + 7) >> 3; + mp_bytes_per_limb = mp_bits_per_limb >> 3; + nb_partial_byte = nb_byte % mp_bytes_per_limb; + nb_limb = (nb_byte + mp_bytes_per_limb - 1) / mp_bytes_per_limb; + + result = buffer; + ALLOC_RESULT(result, buffer_size, nb_byte); + + putBigEndianData (result, (unsigned char*) MPFR_MANT(x), + sizeof(mp_limb_t), nb_partial_byte); + for (i = nb_partial_byte, j = (nb_partial_byte == 0) ? 0 : 1; j < nb_limb; + i += mp_bytes_per_limb, j++) + putLittleEndianData (result + i, (unsigned char*) (MPFR_MANT(x) + j), + sizeof(mp_limb_t), sizeof(mp_limb_t)); + + return result; +} + +/* + * x : OUT : MPFR number extracted from the binary buffer, should have the same + * precision than the number in the binary format + * buffer : IN : limb of the MPFR number x in a binary format, + * buffer_size : IN/OUT : size of the buffer => size used in the buffer + * return 0 if successful + */ +static int +mpfr_fpif_read_limbs (mpfr_t x, unsigned char *buffer, size_t *buffer_size) +{ + mpfr_prec_t precision; + size_t nb_byte; + size_t mp_bytes_per_limb; + size_t nb_partial_byte; + size_t i, j; + + precision = mpfr_get_prec (x); + nb_byte = (precision + 7) >> 3; + mp_bytes_per_limb = mp_bits_per_limb >> 3; + nb_partial_byte = nb_byte % mp_bytes_per_limb; + + if ((buffer == NULL) || (*buffer_size < nb_byte)) + { + *buffer_size = 0; + return 1; + } + *buffer_size = nb_byte; + + if (nb_partial_byte > 0) + { + memset (MPFR_MANT(x), 0, sizeof(mp_limb_t)); + getBigEndianData ((unsigned char*) MPFR_MANT(x), buffer, + sizeof(mp_limb_t), nb_partial_byte); + } + for (i = nb_partial_byte, j = (nb_partial_byte == 0) ? 0 : 1; i < nb_byte; + i += mp_bytes_per_limb, j++) + getLittleEndianData ((unsigned char*) (MPFR_MANT(x) + j), buffer + i, + sizeof(mp_limb_t), sizeof(mp_limb_t)); + + return 0; +} + +/* External Function */ +/* + * fh : IN : file hander + * x : IN : MPFR number to put in the file + * return 0 if successful + */ +int +mpfr_fpif_export (FILE *fh, mpfr_t x) +{ + int status; + unsigned char *buf; + unsigned char *bufResult; + size_t used_size, buf_size; + + if (fh == NULL) + return -1; + + buf_size = MAX_VARIABLE_STORAGE(sizeof(mpfr_exp_t), mpfr_get_prec (x)); + buf = (unsigned char*) mpfr_allocate_func (buf_size); + if (buf == NULL) + return -1; + + used_size = buf_size; + buf = mpfr_fpif_store_precision (buf, &used_size, mpfr_get_prec (x)); + used_size > buf_size ? buf_size = used_size : 0; + status = fwrite (buf, used_size, 1, fh); + if (status != 1) + { + mpfr_free_func (buf, buf_size); + return -1; + } + used_size = buf_size; + bufResult = mpfr_fpif_store_exponent (buf, &used_size, x); + if (bufResult == NULL) + { + mpfr_free_func (buf, buf_size); + return -1; + } + buf = bufResult; + used_size > buf_size ? buf_size = used_size : 0; + status = fwrite (buf, used_size, 1, fh); + if (status != 1) + { + mpfr_free_func (buf, buf_size); + return -1; + } + + if (mpfr_regular_p (x)) + { + used_size = buf_size; + buf = mpfr_fpif_store_limbs (buf, &used_size, x); + used_size > buf_size ? buf_size = used_size : 0; + status = fwrite (buf, used_size, 1, fh); + if (status != 1) + { + mpfr_free_func (buf, buf_size); + return -1; + } + } + + mpfr_free_func (buf, buf_size); + return 0; +} + +/* + * x : IN/OUT : MPFR number extracted from the file, its precision is reset to + * be able to hold the number + * fh : IN : file hander + * Return 0 if the import was successful. + */ +int +mpfr_fpif_import (mpfr_t x, FILE *fh) +{ + int status; + mpfr_prec_t precision; + unsigned char *buffer; + size_t used_size; + + precision = mpfr_fpif_read_precision_from_file (fh); + if (precision == 0) /* precision = 0 means an error */ + return -1; + if (precision > MPFR_PREC_MAX) + return -1; + MPFR_STAT_STATIC_ASSERT (MPFR_PREC_MIN == 1); /* as specified */ + mpfr_set_prec (x, precision); + + status = mpfr_fpif_read_exponent_from_file (x, fh); + if (status != 0) + { + mpfr_set_nan (x); + return -1; + } + + /* Warning! The significand of x is not set yet. Thus use MPFR_IS_SINGULAR + for the test. */ + if (!MPFR_IS_SINGULAR (x)) + { + /* For portability, we need to consider bytes with only 8 significant + bits in the interchange format. That's OK because CHAR_BIT >= 8. + But the implementation is currently not clear when CHAR_BIT > 8. + This may have never been tested. For safety, require CHAR_BIT == 8, + and test/adapt the code if this ever fails. */ + MPFR_STAT_STATIC_ASSERT (CHAR_BIT == 8); + MPFR_STAT_STATIC_ASSERT ((MPFR_PREC_MAX + 7) >> 3 <= (size_t) -1); + used_size = (precision + 7) >> 3; /* ceil(precision/8) */ + buffer = (unsigned char*) mpfr_allocate_func (used_size); + if (buffer == NULL) + { + mpfr_set_nan (x); + return -1; + } + status = fread (buffer, used_size, 1, fh); + if (status != 1) + { + mpfr_free_func (buffer, used_size); + mpfr_set_nan (x); + return -1; + } + status = mpfr_fpif_read_limbs (x, buffer, &used_size); + mpfr_free_func (buffer, used_size); + if (status != 0) + { + mpfr_set_nan (x); + return -1; + } + } + + return 0; +} |