xref: /netbsd-src/external/lgpl3/mpfr/dist/src/fpif.c (revision ba125506a622fe649968631a56eba5d42ff57863)
1299c6f0cSmrg /* mpfr_fpif -- Binary export & import of MPFR numbers
2299c6f0cSmrg    (floating-point interchange format)
3299c6f0cSmrg 
4*ba125506Smrg Copyright 2012-2023 Free Software Foundation, Inc.
5299c6f0cSmrg Contributed by Olivier Demengeon.
6299c6f0cSmrg 
7299c6f0cSmrg This file is part of the GNU MPFR Library.
8299c6f0cSmrg 
9299c6f0cSmrg The GNU MPFR Library is free software; you can redistribute it and/or modify
10299c6f0cSmrg it under the terms of the GNU Lesser General Public License as published by
11299c6f0cSmrg the Free Software Foundation; either version 3 of the License, or (at your
12299c6f0cSmrg option) any later version.
13299c6f0cSmrg 
14299c6f0cSmrg The GNU MPFR Library is distributed in the hope that it will be useful, but
15299c6f0cSmrg WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16299c6f0cSmrg or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
17299c6f0cSmrg License for more details.
18299c6f0cSmrg 
19299c6f0cSmrg You should have received a copy of the GNU Lesser General Public License
20299c6f0cSmrg along with the GNU MPFR Library; see the file COPYING.LESSER.  If not, see
212ba2404bSmrg https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
22299c6f0cSmrg 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
23299c6f0cSmrg 
24299c6f0cSmrg #include "mpfr-impl.h"
25299c6f0cSmrg 
26299c6f0cSmrg #if !defined (HAVE_BIG_ENDIAN) && !defined (HAVE_LITTLE_ENDIAN)
27299c6f0cSmrg #error "Endianness is unknown. Not supported yet."
28299c6f0cSmrg #endif
29299c6f0cSmrg 
30299c6f0cSmrg /* The format is described as follows. Any multi-byte number is encoded
31299c6f0cSmrg    in little endian.
32299c6f0cSmrg 
33299c6f0cSmrg    1. We first store the precision p (this format is able to represent
34299c6f0cSmrg       any precision from 1 to 2^64 + 248).
35299c6f0cSmrg       Let B be the first byte (0 <= B <= 255).
36299c6f0cSmrg         * If B >= 8, the precision p is B-7.
37299c6f0cSmrg           Here, the condition is equivalent to 1 <= p <= 248.
38299c6f0cSmrg         * If B <= 7, the next B+1 bytes contain p-249.
39299c6f0cSmrg           Here, the condition is equivalent to 249 <= p <= 2^64 + 248.
40299c6f0cSmrg       We will use the following macros:
41299c6f0cSmrg         * MPFR_MAX_PRECSIZE = 7
42299c6f0cSmrg         * MPFR_MAX_EMBEDDED_PRECISION = 255 - 7 = 248
43299c6f0cSmrg 
44299c6f0cSmrg    2. Then we store the sign bit and exponent related information
45299c6f0cSmrg       (possibly a special value). We first have byte A = [seeeeeee],
46299c6f0cSmrg       where s is the sign bit and E = [eeeeeee] such that:
47299c6f0cSmrg         * If 0 <= E <= 94, then the exponent e is E-47 (-47 <= e <= 47).
48299c6f0cSmrg         * If 95 <= E <= 110, the exponent is stored in the next E-94 bytes
492ba2404bSmrg           (1 to 16 bytes) in sign + absolute value representation,
502ba2404bSmrg           where the absolute value is increased by 47 (e <= -47 or 47 <= e).
51299c6f0cSmrg         * If 111 <= E <= 118, the exponent size S is stored in the next
52299c6f0cSmrg           E-110 bytes (1 to 8), then the exponent itself is stored in the
53299c6f0cSmrg           next S bytes. [Not implemented yet]
54299c6f0cSmrg         * If 119 <= E <= 127, we have a special value:
55299c6f0cSmrg           E = 119 (MPFR_KIND_ZERO) for a signed zero;
56299c6f0cSmrg           E = 120 (MPFR_KIND_INF) for a signed infinity;
57299c6f0cSmrg           E = 121 (MPFR_KIND_NAN) for NaN.
58299c6f0cSmrg 
59299c6f0cSmrg    3. Then we store the significand (for regular values).
60299c6f0cSmrg 
61299c6f0cSmrg    The sign bit is preserved by the import/export functions, even for NaN.
62299c6f0cSmrg 
63299c6f0cSmrg    Note: When a size is stored, it must be minimal, i.e. a number cannot
64299c6f0cSmrg    start with a null byte. Otherwise the import may fail.
65299c6f0cSmrg */
66299c6f0cSmrg 
67299c6f0cSmrg #define MPFR_MAX_PRECSIZE 7
68299c6f0cSmrg #define MPFR_MAX_EMBEDDED_PRECISION (255 - MPFR_MAX_PRECSIZE)
69299c6f0cSmrg 
70299c6f0cSmrg #define MPFR_KIND_ZERO 119
71299c6f0cSmrg #define MPFR_KIND_INF 120
72299c6f0cSmrg #define MPFR_KIND_NAN 121
73299c6f0cSmrg #define MPFR_MAX_EMBEDDED_EXPONENT 47
74299c6f0cSmrg #define MPFR_EXTERNAL_EXPONENT 94
75299c6f0cSmrg 
76299c6f0cSmrg /* Begin: Low level helper functions */
77299c6f0cSmrg 
78299c6f0cSmrg /* storage must have an unsigned type */
79299c6f0cSmrg #define COUNT_NB_BYTE(storage, size)            \
80299c6f0cSmrg   do                                            \
81299c6f0cSmrg     {                                           \
82299c6f0cSmrg       (storage) >>= 8;                          \
83299c6f0cSmrg       (size)++;                                 \
84299c6f0cSmrg     }                                           \
85299c6f0cSmrg   while ((storage) != 0)
86299c6f0cSmrg 
87299c6f0cSmrg #define ALLOC_RESULT(buffer, buffer_size, wanted_size)                  \
88299c6f0cSmrg   do                                                                    \
89299c6f0cSmrg     {                                                                   \
90299c6f0cSmrg       if ((buffer) == NULL || *(buffer_size) < (wanted_size))           \
91299c6f0cSmrg         {                                                               \
92299c6f0cSmrg           (buffer) = (unsigned char *) mpfr_reallocate_func             \
93299c6f0cSmrg             ((buffer), *(buffer_size), (wanted_size));                  \
942ba2404bSmrg           MPFR_ASSERTN((buffer) != 0);                                  \
95299c6f0cSmrg         }                                                               \
96299c6f0cSmrg       *(buffer_size) = (wanted_size);                                   \
97299c6f0cSmrg     }                                                                   \
98299c6f0cSmrg   while (0)
99299c6f0cSmrg 
100299c6f0cSmrg /*
101299c6f0cSmrg  * size in byte of a MPFR number in a binary object of a variable size
102299c6f0cSmrg  */
103299c6f0cSmrg #define MAX_VARIABLE_STORAGE(exponent_size, precision) \
104299c6f0cSmrg   ((size_t)(((precision) >> 3) + (exponent_size) +     \
105299c6f0cSmrg             ((precision) > 248 ? sizeof(mpfr_prec_t) : 0) + 3))
106299c6f0cSmrg 
107299c6f0cSmrg /* copy in result[] the values in data[] with a different endianness,
108299c6f0cSmrg    where data_size might be smaller than data_max_size, so that we only
109299c6f0cSmrg    copy data_size bytes from the end of data[]. */
110299c6f0cSmrg static void
111299c6f0cSmrg #if defined (HAVE_BIG_ENDIAN)
putLittleEndianData(unsigned char * result,unsigned char * data,size_t data_max_size,size_t data_size)112299c6f0cSmrg putLittleEndianData (unsigned char *result, unsigned char *data,
113299c6f0cSmrg                      size_t data_max_size, size_t data_size)
114299c6f0cSmrg #elif defined (HAVE_LITTLE_ENDIAN)
115299c6f0cSmrg putBigEndianData (unsigned char *result, unsigned char *data,
116299c6f0cSmrg                   size_t data_max_size, size_t data_size)
117299c6f0cSmrg #endif
118299c6f0cSmrg {
119299c6f0cSmrg   size_t j;
120299c6f0cSmrg 
121299c6f0cSmrg   MPFR_ASSERTD (data_size <= data_max_size);
122299c6f0cSmrg   for (j = 0; j < data_size; j++)
123299c6f0cSmrg     result[j] = data[data_max_size - j - 1];
124299c6f0cSmrg }
125299c6f0cSmrg 
126299c6f0cSmrg /* copy in result[] the values in data[] with the same endianness */
127299c6f0cSmrg static void
128299c6f0cSmrg #if defined (HAVE_BIG_ENDIAN)
putBigEndianData(unsigned char * result,unsigned char * data,size_t data_max_size,size_t data_size)129299c6f0cSmrg putBigEndianData (unsigned char *result, unsigned char *data,
130299c6f0cSmrg                   size_t data_max_size, size_t data_size)
131299c6f0cSmrg #elif defined (HAVE_LITTLE_ENDIAN)
132299c6f0cSmrg putLittleEndianData (unsigned char *result, unsigned char *data,
133299c6f0cSmrg                      size_t data_max_size, size_t data_size)
134299c6f0cSmrg #endif
135299c6f0cSmrg {
136299c6f0cSmrg   MPFR_ASSERTD (data_size <= data_max_size);
137299c6f0cSmrg   memcpy (result, data, data_size);
138299c6f0cSmrg }
139299c6f0cSmrg 
140299c6f0cSmrg /* copy in result[] the values in data[] with a different endianness;
141299c6f0cSmrg    the data are written at the end of the result[] buffer (if
142299c6f0cSmrg    data_size < data_max_size, the first bytes of result[] are
143299c6f0cSmrg    left untouched). */
144299c6f0cSmrg static void
145299c6f0cSmrg #if defined (HAVE_BIG_ENDIAN)
getLittleEndianData(unsigned char * result,unsigned char * data,size_t data_max_size,size_t data_size)146299c6f0cSmrg getLittleEndianData (unsigned char *result, unsigned char *data,
147299c6f0cSmrg                      size_t data_max_size, size_t data_size)
148299c6f0cSmrg #elif defined (HAVE_LITTLE_ENDIAN)
149299c6f0cSmrg getBigEndianData (unsigned char *result, unsigned char *data,
150299c6f0cSmrg                   size_t data_max_size, size_t data_size)
151299c6f0cSmrg #endif
152299c6f0cSmrg {
153299c6f0cSmrg   size_t j;
154299c6f0cSmrg 
155299c6f0cSmrg   MPFR_ASSERTD (data_size <= data_max_size);
156299c6f0cSmrg   for (j = 0; j < data_size; j++)
157299c6f0cSmrg     result[data_max_size - j - 1] = data[j];
158299c6f0cSmrg }
159299c6f0cSmrg 
160299c6f0cSmrg /* copy in result[] the values in data[] with the same endianness */
161299c6f0cSmrg static void
162299c6f0cSmrg #if defined (HAVE_BIG_ENDIAN)
getBigEndianData(unsigned char * result,unsigned char * data,size_t data_max_size,size_t data_size)163299c6f0cSmrg getBigEndianData (unsigned char *result, unsigned char *data,
164299c6f0cSmrg                   size_t data_max_size, size_t data_size)
165299c6f0cSmrg #elif defined (HAVE_LITTLE_ENDIAN)
166299c6f0cSmrg getLittleEndianData (unsigned char *result, unsigned char *data,
167299c6f0cSmrg                      size_t data_max_size, size_t data_size)
168299c6f0cSmrg #endif
169299c6f0cSmrg {
170299c6f0cSmrg   MPFR_ASSERTD (data_size <= data_max_size);
171299c6f0cSmrg   memcpy (result, data, data_size);
172299c6f0cSmrg }
173299c6f0cSmrg 
174299c6f0cSmrg /* End: Low level helper functions */
175299c6f0cSmrg 
176299c6f0cSmrg /* Internal Function */
177299c6f0cSmrg /*
178299c6f0cSmrg  * buffer : OUT : store the precision in binary format, can be null
179299c6f0cSmrg  *               (may be reallocated if too small)
180299c6f0cSmrg  * buffer_size : IN/OUT : size of the buffer => size used in the buffer
181299c6f0cSmrg  * precision : IN : precision to store
182299c6f0cSmrg  * return pointer to a buffer storing the precision in binary format
183299c6f0cSmrg  */
184299c6f0cSmrg static unsigned char *
mpfr_fpif_store_precision(unsigned char * buffer,size_t * buffer_size,mpfr_prec_t precision)185299c6f0cSmrg mpfr_fpif_store_precision (unsigned char *buffer, size_t *buffer_size,
186299c6f0cSmrg                            mpfr_prec_t precision)
187299c6f0cSmrg {
188299c6f0cSmrg   unsigned char *result;
189299c6f0cSmrg   size_t size_precision;
190299c6f0cSmrg 
191299c6f0cSmrg   MPFR_ASSERTD (precision >= 1);
192299c6f0cSmrg   size_precision = 0;
193299c6f0cSmrg 
194299c6f0cSmrg   if (precision > MPFR_MAX_EMBEDDED_PRECISION)
195299c6f0cSmrg     {
196299c6f0cSmrg       mpfr_uprec_t copy_precision;
197299c6f0cSmrg 
198299c6f0cSmrg       copy_precision = precision - (MPFR_MAX_EMBEDDED_PRECISION + 1);
199299c6f0cSmrg       COUNT_NB_BYTE(copy_precision, size_precision);
200299c6f0cSmrg     }
201299c6f0cSmrg 
202299c6f0cSmrg   result = buffer;
203299c6f0cSmrg   ALLOC_RESULT(result, buffer_size, size_precision + 1);
204299c6f0cSmrg 
205299c6f0cSmrg   if (precision > MPFR_MAX_EMBEDDED_PRECISION)
206299c6f0cSmrg     {
207299c6f0cSmrg       result[0] = size_precision - 1;
208299c6f0cSmrg       precision -= (MPFR_MAX_EMBEDDED_PRECISION + 1);
209299c6f0cSmrg       putLittleEndianData (result + 1, (unsigned char *) &precision,
210299c6f0cSmrg                            sizeof(mpfr_prec_t), size_precision);
211299c6f0cSmrg     }
212299c6f0cSmrg   else
213299c6f0cSmrg     result[0] = precision + MPFR_MAX_PRECSIZE;
214299c6f0cSmrg 
215299c6f0cSmrg   return result;
216299c6f0cSmrg }
217299c6f0cSmrg 
218299c6f0cSmrg #define BUFFER_SIZE 8
219299c6f0cSmrg 
220299c6f0cSmrg /*
221299c6f0cSmrg  * fh : IN : file handler
222299c6f0cSmrg  * return the precision stored in the binary buffer, 0 in case of error
223299c6f0cSmrg  */
224299c6f0cSmrg static mpfr_prec_t
mpfr_fpif_read_precision_from_file(FILE * fh)225299c6f0cSmrg mpfr_fpif_read_precision_from_file (FILE *fh)
226299c6f0cSmrg {
227299c6f0cSmrg   mpfr_prec_t precision;
228299c6f0cSmrg   size_t precision_size;
229299c6f0cSmrg   unsigned char buffer[BUFFER_SIZE];
230299c6f0cSmrg 
231299c6f0cSmrg   if (fh == NULL)
232299c6f0cSmrg     return 0;
233299c6f0cSmrg 
234299c6f0cSmrg   if (fread (buffer, 1, 1, fh) != 1)
235299c6f0cSmrg     return 0;
236299c6f0cSmrg 
237299c6f0cSmrg   precision_size = buffer[0];
238299c6f0cSmrg   if (precision_size > MPFR_MAX_PRECSIZE)
239299c6f0cSmrg     return precision_size - MPFR_MAX_PRECSIZE;
240299c6f0cSmrg 
241299c6f0cSmrg   precision_size++;
242299c6f0cSmrg   MPFR_ASSERTD (precision_size <= BUFFER_SIZE);
243299c6f0cSmrg 
244299c6f0cSmrg   /* Read the precision in little-endian format. */
245299c6f0cSmrg   if (fread (buffer, precision_size, 1, fh) != 1)
246299c6f0cSmrg     return 0;
247299c6f0cSmrg 
2482ba2404bSmrg   /* Justification of the #if below. */
2492ba2404bSmrg   MPFR_ASSERTD (precision_size <= MPFR_MAX_PRECSIZE + 1);
2502ba2404bSmrg 
2512ba2404bSmrg #if (MPFR_MAX_PRECSIZE + 1) * CHAR_BIT > MPFR_PREC_BITS
252299c6f0cSmrg   while (precision_size > sizeof(mpfr_prec_t))
253299c6f0cSmrg     {
254299c6f0cSmrg       if (buffer[precision_size-1] != 0)
255299c6f0cSmrg         return 0;  /* the read precision doesn't fit in a mpfr_prec_t */
256299c6f0cSmrg       precision_size--;
257299c6f0cSmrg     }
2582ba2404bSmrg #endif
259299c6f0cSmrg 
2602ba2404bSmrg   /* To detect bugs affecting particular platforms (thus MPFR_ASSERTN)... */
2612ba2404bSmrg   MPFR_ASSERTN (precision_size <= sizeof(mpfr_prec_t));
2622ba2404bSmrg 
2632ba2404bSmrg   /* Since mpfr_prec_t is signed, one also needs to check that the
2642ba2404bSmrg      most significant bit of the corresponding unsigned value is 0. */
265299c6f0cSmrg   if (precision_size == sizeof(mpfr_prec_t) &&
266299c6f0cSmrg       buffer[precision_size-1] >= 0x80)
267299c6f0cSmrg     return 0;  /* the read precision doesn't fit in a mpfr_prec_t */
268299c6f0cSmrg 
269299c6f0cSmrg   precision = 0;  /* to pad with 0's if data_size < data_max_size */
270299c6f0cSmrg 
271299c6f0cSmrg   /* On big-endian machines, the data must be copied at the end of the
272299c6f0cSmrg      precision object in the memory; thus data_max_size (3rd argument)
273299c6f0cSmrg      must be sizeof(mpfr_prec_t). */
274299c6f0cSmrg   getLittleEndianData ((unsigned char *) &precision, buffer,
275299c6f0cSmrg                        sizeof(mpfr_prec_t), precision_size);
276299c6f0cSmrg 
277299c6f0cSmrg   return precision + (MPFR_MAX_EMBEDDED_PRECISION + 1);
278299c6f0cSmrg }
279299c6f0cSmrg 
280299c6f0cSmrg /*
281299c6f0cSmrg  * buffer : OUT : store the kind of the MPFR number x, its sign, the size of
282299c6f0cSmrg  *                its exponent and its exponent value in a binary format,
283299c6f0cSmrg  *                can be null (may be reallocated if too small)
284299c6f0cSmrg  * buffer_size : IN/OUT : size of the buffer => size used in the buffer
285299c6f0cSmrg  * x : IN : MPFR number
286299c6f0cSmrg  * return pointer to a buffer storing the kind of the MPFR number x, its sign,
287299c6f0cSmrg  *        the size of its exponent and its exponent value in a binary format,
288299c6f0cSmrg  */
289299c6f0cSmrg /* TODO
290299c6f0cSmrg  *   Exponents that use more than 16 bytes are not managed (not an issue
291299c6f0cSmrg  *   until one has integer types larger than 128 bits).
292299c6f0cSmrg  */
293299c6f0cSmrg static unsigned char*
mpfr_fpif_store_exponent(unsigned char * buffer,size_t * buffer_size,mpfr_ptr x)294*ba125506Smrg mpfr_fpif_store_exponent (unsigned char *buffer, size_t *buffer_size,
295*ba125506Smrg                           mpfr_ptr x)
296299c6f0cSmrg {
297299c6f0cSmrg   unsigned char *result;
298299c6f0cSmrg   mpfr_uexp_t uexp;
299299c6f0cSmrg   size_t exponent_size;
300299c6f0cSmrg 
301299c6f0cSmrg   exponent_size = 0;
302299c6f0cSmrg 
303299c6f0cSmrg   if (MPFR_IS_PURE_FP (x))
304299c6f0cSmrg     {
305299c6f0cSmrg       mpfr_exp_t exponent = MPFR_GET_EXP (x);
306299c6f0cSmrg 
307299c6f0cSmrg       if (exponent > MPFR_MAX_EMBEDDED_EXPONENT ||
308299c6f0cSmrg           exponent < -MPFR_MAX_EMBEDDED_EXPONENT)
309299c6f0cSmrg         {
310299c6f0cSmrg           mpfr_uexp_t copy_exponent, exp_sign_bit;
311299c6f0cSmrg 
312299c6f0cSmrg           uexp = SAFE_ABS (mpfr_uexp_t, exponent)
313299c6f0cSmrg             - MPFR_MAX_EMBEDDED_EXPONENT;
314299c6f0cSmrg 
315299c6f0cSmrg           /* Shift uexp to take the sign bit of the exponent into account.
316299c6f0cSmrg              Because of constraints on the valid exponents, this cannot
317299c6f0cSmrg              overflow (check with an MPFR_ASSERTD). */
318299c6f0cSmrg           copy_exponent = uexp << 1;
319299c6f0cSmrg           MPFR_ASSERTD (copy_exponent > uexp);
320299c6f0cSmrg           COUNT_NB_BYTE(copy_exponent, exponent_size);
321299c6f0cSmrg           MPFR_ASSERTN (exponent_size <= 16);  /* see TODO */
322299c6f0cSmrg 
323299c6f0cSmrg           /* Sign bit of the exponent. */
324299c6f0cSmrg           exp_sign_bit = (mpfr_uexp_t) 1 << (8 * exponent_size - 1);
325299c6f0cSmrg           MPFR_ASSERTD (uexp < exp_sign_bit);
326299c6f0cSmrg           if (exponent < 0)
327299c6f0cSmrg             uexp |= exp_sign_bit;
328299c6f0cSmrg         }
329299c6f0cSmrg       else
330299c6f0cSmrg         uexp = exponent + MPFR_MAX_EMBEDDED_EXPONENT;
331299c6f0cSmrg     }
332299c6f0cSmrg 
333299c6f0cSmrg   result = buffer;
334299c6f0cSmrg   ALLOC_RESULT(result, buffer_size, exponent_size + 1);
335299c6f0cSmrg 
336299c6f0cSmrg   if (MPFR_IS_PURE_FP (x))
337299c6f0cSmrg     {
338299c6f0cSmrg       if (exponent_size == 0)
339299c6f0cSmrg         result[0] = uexp;
340299c6f0cSmrg       else
341299c6f0cSmrg         {
342299c6f0cSmrg           result[0] = MPFR_EXTERNAL_EXPONENT + exponent_size;
343299c6f0cSmrg 
344299c6f0cSmrg           putLittleEndianData (result + 1, (unsigned char *) &uexp,
345299c6f0cSmrg                                sizeof(mpfr_exp_t), exponent_size);
346299c6f0cSmrg         }
347299c6f0cSmrg     }
348299c6f0cSmrg   else if (MPFR_IS_ZERO (x))
349299c6f0cSmrg     result[0] = MPFR_KIND_ZERO;
350299c6f0cSmrg   else if (MPFR_IS_INF (x))
351299c6f0cSmrg     result[0] = MPFR_KIND_INF;
352299c6f0cSmrg   else
353299c6f0cSmrg     {
354299c6f0cSmrg       MPFR_ASSERTD (MPFR_IS_NAN (x));
355299c6f0cSmrg       result[0] = MPFR_KIND_NAN;
356299c6f0cSmrg     }
357299c6f0cSmrg 
358299c6f0cSmrg   /* Set the sign, even for NaN. */
359299c6f0cSmrg   if (MPFR_IS_NEG (x))
360299c6f0cSmrg     result[0] |= 0x80;
361299c6f0cSmrg 
362299c6f0cSmrg   return result;
363299c6f0cSmrg }
364299c6f0cSmrg 
365299c6f0cSmrg /*
366299c6f0cSmrg  * x : OUT : MPFR number extracted from the binary buffer
3672ba2404bSmrg  * fh : IN : file handler (should not be NULL)
368299c6f0cSmrg  * return 0 if successful
369299c6f0cSmrg  */
370299c6f0cSmrg /* TODO
371299c6f0cSmrg  *   Exponents that use more than 16 bytes are not managed (this is not
372299c6f0cSmrg  *   an issue if the data were written by MPFR with mpfr_exp_t not larger
373299c6f0cSmrg  *   than 128 bits).
374299c6f0cSmrg  */
375299c6f0cSmrg static int
mpfr_fpif_read_exponent_from_file(mpfr_ptr x,FILE * fh)376*ba125506Smrg mpfr_fpif_read_exponent_from_file (mpfr_ptr x, FILE * fh)
377299c6f0cSmrg {
378299c6f0cSmrg   mpfr_exp_t exponent;
379299c6f0cSmrg   mpfr_uexp_t uexp;
380299c6f0cSmrg   size_t exponent_size;
381299c6f0cSmrg   int sign;
382299c6f0cSmrg   unsigned char buffer[sizeof(mpfr_exp_t)];
383299c6f0cSmrg 
3842ba2404bSmrg   MPFR_ASSERTD(fh != NULL);
385299c6f0cSmrg 
386299c6f0cSmrg   if (fread (buffer, 1, 1, fh) != 1)
387299c6f0cSmrg     return 1;
388299c6f0cSmrg 
389299c6f0cSmrg   /* sign value that can be used with MPFR_SET_SIGN,
390299c6f0cSmrg      mpfr_set_zero and mpfr_set_inf */
391299c6f0cSmrg   sign = (buffer[0] & 0x80) ? MPFR_SIGN_NEG : MPFR_SIGN_POS;
392299c6f0cSmrg   /* Set the sign, even for NaN. */
393299c6f0cSmrg   MPFR_SET_SIGN (x, sign);
394299c6f0cSmrg 
395299c6f0cSmrg   exponent = buffer[0] & 0x7F;
396299c6f0cSmrg   exponent_size = 1;
397299c6f0cSmrg 
398299c6f0cSmrg   if (exponent > MPFR_EXTERNAL_EXPONENT && exponent < MPFR_KIND_ZERO)
399299c6f0cSmrg     {
400299c6f0cSmrg       mpfr_uexp_t exp_sign_bit;
401299c6f0cSmrg 
402299c6f0cSmrg       exponent_size = exponent - MPFR_EXTERNAL_EXPONENT;
403299c6f0cSmrg 
404299c6f0cSmrg       /* A failure is acceptable when the exponent starts with leading zeros,
405299c6f0cSmrg          even if it would fit in mpfr_exp_t (see format description). */
406299c6f0cSmrg       if (MPFR_UNLIKELY (exponent_size > 16 /* see TODO */ ||
407299c6f0cSmrg                          exponent_size > sizeof(mpfr_exp_t)))
408299c6f0cSmrg         return 1;
409299c6f0cSmrg 
410299c6f0cSmrg       if (MPFR_UNLIKELY (fread (buffer, exponent_size, 1, fh) != 1))
411299c6f0cSmrg         return 1;
412299c6f0cSmrg 
413299c6f0cSmrg       uexp = 0;
414299c6f0cSmrg       getLittleEndianData ((unsigned char *) &uexp, buffer,
415299c6f0cSmrg                            sizeof(mpfr_exp_t), exponent_size);
416299c6f0cSmrg 
417299c6f0cSmrg       /* Sign bit of the exponent. */
418299c6f0cSmrg       exp_sign_bit = uexp & ((mpfr_uexp_t) 1 << (8 * exponent_size - 1));
419299c6f0cSmrg 
420299c6f0cSmrg       uexp &= ~exp_sign_bit;
421299c6f0cSmrg       uexp += MPFR_MAX_EMBEDDED_EXPONENT;
422299c6f0cSmrg       if (MPFR_UNLIKELY (uexp > MPFR_EMAX_MAX && uexp > -MPFR_EMIN_MIN))
423299c6f0cSmrg         return 1;
424299c6f0cSmrg 
425299c6f0cSmrg       exponent = exp_sign_bit ? - (mpfr_exp_t) uexp : (mpfr_exp_t) uexp;
426299c6f0cSmrg       if (MPFR_UNLIKELY (! MPFR_EXP_IN_RANGE (exponent)))
427299c6f0cSmrg         return 1;
428299c6f0cSmrg       MPFR_SET_EXP (x, exponent);
429299c6f0cSmrg 
430299c6f0cSmrg       exponent_size++;
431299c6f0cSmrg     }
432299c6f0cSmrg   else if (exponent == MPFR_KIND_ZERO)
433299c6f0cSmrg     MPFR_SET_ZERO (x);
434299c6f0cSmrg   else if (exponent == MPFR_KIND_INF)
435299c6f0cSmrg     MPFR_SET_INF (x);
436299c6f0cSmrg   else if (exponent == MPFR_KIND_NAN)
437299c6f0cSmrg     MPFR_SET_NAN (x);
438299c6f0cSmrg   else if (exponent <= MPFR_EXTERNAL_EXPONENT)
439299c6f0cSmrg     {
440299c6f0cSmrg       exponent -= MPFR_MAX_EMBEDDED_EXPONENT;
441299c6f0cSmrg       if (MPFR_UNLIKELY (! MPFR_EXP_IN_RANGE (exponent)))
442299c6f0cSmrg         return 1;
443299c6f0cSmrg       MPFR_SET_EXP (x, exponent);
444299c6f0cSmrg     }
445299c6f0cSmrg   else
446299c6f0cSmrg     return 1;
447299c6f0cSmrg 
448299c6f0cSmrg   return 0;
449299c6f0cSmrg }
450299c6f0cSmrg 
451299c6f0cSmrg /*
452299c6f0cSmrg  * buffer : OUT : store the limb of the MPFR number x in a binary format,
453299c6f0cSmrg  *                can be null (may be reallocated if too small)
454299c6f0cSmrg  * buffer_size : IN/OUT : size of the buffer => size used in the buffer
455299c6f0cSmrg  * x : IN : MPFR number
456299c6f0cSmrg  * return pointer to a buffer storing the limb of the MPFR number x in a binary
457299c6f0cSmrg  *        format
458299c6f0cSmrg  */
459299c6f0cSmrg static unsigned char*
mpfr_fpif_store_limbs(unsigned char * buffer,size_t * buffer_size,mpfr_ptr x)460*ba125506Smrg mpfr_fpif_store_limbs (unsigned char *buffer, size_t *buffer_size, mpfr_ptr x)
461299c6f0cSmrg {
462299c6f0cSmrg   unsigned char *result;
463299c6f0cSmrg   mpfr_prec_t precision;
464299c6f0cSmrg   size_t nb_byte;
465299c6f0cSmrg   size_t nb_limb, mp_bytes_per_limb;
466299c6f0cSmrg   size_t nb_partial_byte;
467299c6f0cSmrg   size_t i, j;
468299c6f0cSmrg 
469299c6f0cSmrg   precision = mpfr_get_prec (x);
470299c6f0cSmrg   nb_byte = (precision + 7) >> 3;
471299c6f0cSmrg   mp_bytes_per_limb = mp_bits_per_limb >> 3;
472299c6f0cSmrg   nb_partial_byte = nb_byte % mp_bytes_per_limb;
473299c6f0cSmrg   nb_limb = (nb_byte + mp_bytes_per_limb - 1) / mp_bytes_per_limb;
474299c6f0cSmrg 
475299c6f0cSmrg   result = buffer;
476299c6f0cSmrg   ALLOC_RESULT(result, buffer_size, nb_byte);
477299c6f0cSmrg 
478299c6f0cSmrg   putBigEndianData (result, (unsigned char*) MPFR_MANT(x),
479299c6f0cSmrg                     sizeof(mp_limb_t), nb_partial_byte);
480299c6f0cSmrg   for (i = nb_partial_byte, j = (nb_partial_byte == 0) ? 0 : 1; j < nb_limb;
481299c6f0cSmrg        i += mp_bytes_per_limb, j++)
482299c6f0cSmrg     putLittleEndianData (result + i, (unsigned char*) (MPFR_MANT(x) + j),
483299c6f0cSmrg                          sizeof(mp_limb_t), sizeof(mp_limb_t));
484299c6f0cSmrg 
485299c6f0cSmrg   return result;
486299c6f0cSmrg }
487299c6f0cSmrg 
488299c6f0cSmrg /*
489299c6f0cSmrg  * x : OUT : MPFR number extracted from the binary buffer, should have the same
490299c6f0cSmrg  *           precision than the number in the binary format
4912ba2404bSmrg  * buffer : IN : limb of the MPFR number x in a binary format
4922ba2404bSmrg  * nb_byte : IN : size of the buffer (in bytes)
4932ba2404bSmrg  * Assume buffer is not NULL.
494299c6f0cSmrg  */
4952ba2404bSmrg static void
mpfr_fpif_read_limbs(mpfr_ptr x,unsigned char * buffer,size_t nb_byte)496*ba125506Smrg mpfr_fpif_read_limbs (mpfr_ptr x, unsigned char *buffer, size_t nb_byte)
497299c6f0cSmrg {
498299c6f0cSmrg   size_t mp_bytes_per_limb;
499299c6f0cSmrg   size_t nb_partial_byte;
500299c6f0cSmrg   size_t i, j;
501299c6f0cSmrg 
5022ba2404bSmrg   MPFR_ASSERTD (buffer != NULL);
5032ba2404bSmrg 
504299c6f0cSmrg   mp_bytes_per_limb = mp_bits_per_limb >> 3;
505299c6f0cSmrg   nb_partial_byte = nb_byte % mp_bytes_per_limb;
506299c6f0cSmrg 
507299c6f0cSmrg   if (nb_partial_byte > 0)
508299c6f0cSmrg     {
509299c6f0cSmrg       memset (MPFR_MANT(x), 0, sizeof(mp_limb_t));
510299c6f0cSmrg       getBigEndianData ((unsigned char*) MPFR_MANT(x), buffer,
511299c6f0cSmrg                         sizeof(mp_limb_t), nb_partial_byte);
512299c6f0cSmrg     }
513299c6f0cSmrg   for (i = nb_partial_byte, j = (nb_partial_byte == 0) ? 0 : 1; i < nb_byte;
514299c6f0cSmrg        i += mp_bytes_per_limb, j++)
515299c6f0cSmrg     getLittleEndianData ((unsigned char*) (MPFR_MANT(x) + j), buffer + i,
516299c6f0cSmrg                          sizeof(mp_limb_t), sizeof(mp_limb_t));
517299c6f0cSmrg }
518299c6f0cSmrg 
519299c6f0cSmrg /* External Function */
520299c6f0cSmrg /*
5212ba2404bSmrg  * fh : IN : file handler
522299c6f0cSmrg  * x : IN : MPFR number to put in the file
523299c6f0cSmrg  * return 0 if successful
524299c6f0cSmrg  */
525299c6f0cSmrg int
mpfr_fpif_export(FILE * fh,mpfr_ptr x)526*ba125506Smrg mpfr_fpif_export (FILE *fh, mpfr_ptr x)
527299c6f0cSmrg {
528299c6f0cSmrg   int status;
529299c6f0cSmrg   unsigned char *buf;
530299c6f0cSmrg   unsigned char *bufResult;
531299c6f0cSmrg   size_t used_size, buf_size;
532299c6f0cSmrg 
533299c6f0cSmrg   if (fh == NULL)
534299c6f0cSmrg     return -1;
535299c6f0cSmrg 
536299c6f0cSmrg   buf_size = MAX_VARIABLE_STORAGE(sizeof(mpfr_exp_t), mpfr_get_prec (x));
537299c6f0cSmrg   buf = (unsigned char*) mpfr_allocate_func (buf_size);
5382ba2404bSmrg   MPFR_ASSERTN(buf != NULL);
539299c6f0cSmrg 
540299c6f0cSmrg   used_size = buf_size;
541299c6f0cSmrg   buf = mpfr_fpif_store_precision (buf, &used_size, mpfr_get_prec (x));
542299c6f0cSmrg   used_size > buf_size ? buf_size = used_size : 0;
543299c6f0cSmrg   status = fwrite (buf, used_size, 1, fh);
544299c6f0cSmrg   if (status != 1)
545299c6f0cSmrg     {
546299c6f0cSmrg       mpfr_free_func (buf, buf_size);
547299c6f0cSmrg       return -1;
548299c6f0cSmrg     }
549299c6f0cSmrg   used_size = buf_size;
550299c6f0cSmrg   bufResult = mpfr_fpif_store_exponent (buf, &used_size, x);
5512ba2404bSmrg   /* bufResult cannot be NULL: if reallocation failed in
5522ba2404bSmrg      mpfr_fpif_store_exponent, an assertion failed */
553299c6f0cSmrg   buf = bufResult;
554299c6f0cSmrg   used_size > buf_size ? buf_size = used_size : 0;
555299c6f0cSmrg   status = fwrite (buf, used_size, 1, fh);
556299c6f0cSmrg   if (status != 1)
557299c6f0cSmrg     {
558299c6f0cSmrg       mpfr_free_func (buf, buf_size);
559299c6f0cSmrg       return -1;
560299c6f0cSmrg     }
561299c6f0cSmrg 
562299c6f0cSmrg   if (mpfr_regular_p (x))
563299c6f0cSmrg     {
564299c6f0cSmrg       used_size = buf_size;
565299c6f0cSmrg       buf = mpfr_fpif_store_limbs (buf, &used_size, x);
566299c6f0cSmrg       used_size > buf_size ? buf_size = used_size : 0;
567299c6f0cSmrg       status = fwrite (buf, used_size, 1, fh);
568299c6f0cSmrg       if (status != 1)
569299c6f0cSmrg         {
570299c6f0cSmrg           mpfr_free_func (buf, buf_size);
571299c6f0cSmrg           return -1;
572299c6f0cSmrg         }
573299c6f0cSmrg     }
574299c6f0cSmrg 
575299c6f0cSmrg   mpfr_free_func (buf, buf_size);
576299c6f0cSmrg   return 0;
577299c6f0cSmrg }
578299c6f0cSmrg 
579299c6f0cSmrg /*
580299c6f0cSmrg  * x : IN/OUT : MPFR number extracted from the file, its precision is reset to
581299c6f0cSmrg  *              be able to hold the number
5822ba2404bSmrg  * fh : IN : file handler
583299c6f0cSmrg  * Return 0 if the import was successful.
584299c6f0cSmrg  */
585299c6f0cSmrg int
mpfr_fpif_import(mpfr_ptr x,FILE * fh)586*ba125506Smrg mpfr_fpif_import (mpfr_ptr x, FILE *fh)
587299c6f0cSmrg {
588299c6f0cSmrg   int status;
589299c6f0cSmrg   mpfr_prec_t precision;
590299c6f0cSmrg   unsigned char *buffer;
591299c6f0cSmrg   size_t used_size;
592299c6f0cSmrg 
593299c6f0cSmrg   precision = mpfr_fpif_read_precision_from_file (fh);
594299c6f0cSmrg   if (precision == 0) /* precision = 0 means an error */
595299c6f0cSmrg     return -1;
5962ba2404bSmrg   MPFR_ASSERTD(fh != NULL); /* checked by mpfr_fpif_read_precision_from_file */
597299c6f0cSmrg   if (precision > MPFR_PREC_MAX)
598299c6f0cSmrg     return -1;
599299c6f0cSmrg   MPFR_STAT_STATIC_ASSERT (MPFR_PREC_MIN == 1);  /* as specified */
600299c6f0cSmrg   mpfr_set_prec (x, precision);
601299c6f0cSmrg 
602299c6f0cSmrg   status = mpfr_fpif_read_exponent_from_file (x, fh);
603299c6f0cSmrg   if (status != 0)
604299c6f0cSmrg     {
605299c6f0cSmrg       mpfr_set_nan (x);
606299c6f0cSmrg       return -1;
607299c6f0cSmrg     }
608299c6f0cSmrg 
609299c6f0cSmrg   /* Warning! The significand of x is not set yet. Thus use MPFR_IS_SINGULAR
610299c6f0cSmrg      for the test. */
611299c6f0cSmrg   if (!MPFR_IS_SINGULAR (x))
612299c6f0cSmrg     {
613299c6f0cSmrg       /* For portability, we need to consider bytes with only 8 significant
614299c6f0cSmrg          bits in the interchange format. That's OK because CHAR_BIT >= 8.
615299c6f0cSmrg          But the implementation is currently not clear when CHAR_BIT > 8.
616299c6f0cSmrg          This may have never been tested. For safety, require CHAR_BIT == 8,
617299c6f0cSmrg          and test/adapt the code if this ever fails. */
618299c6f0cSmrg       MPFR_STAT_STATIC_ASSERT (CHAR_BIT == 8);
619299c6f0cSmrg       MPFR_STAT_STATIC_ASSERT ((MPFR_PREC_MAX + 7) >> 3 <= (size_t) -1);
620299c6f0cSmrg       used_size = (precision + 7) >> 3; /* ceil(precision/8) */
621299c6f0cSmrg       buffer = (unsigned char*) mpfr_allocate_func (used_size);
6222ba2404bSmrg       MPFR_ASSERTN(buffer != NULL);
623299c6f0cSmrg       status = fread (buffer, used_size, 1, fh);
624299c6f0cSmrg       if (status != 1)
625299c6f0cSmrg         {
626299c6f0cSmrg           mpfr_free_func (buffer, used_size);
627299c6f0cSmrg           mpfr_set_nan (x);
628299c6f0cSmrg           return -1;
629299c6f0cSmrg         }
6302ba2404bSmrg       mpfr_fpif_read_limbs (x, buffer, used_size);
631299c6f0cSmrg       mpfr_free_func (buffer, used_size);
632299c6f0cSmrg     }
633299c6f0cSmrg 
634299c6f0cSmrg   return 0;
635299c6f0cSmrg }
636