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