xref: /dragonfly/contrib/gmp/mpz/inp_raw.c (revision 8af44722)
1 /* mpz_inp_raw -- read an mpz_t in raw format.
2 
3 Copyright 2001, 2002, 2005 Free Software Foundation, Inc.
4 
5 This file is part of the GNU MP Library.
6 
7 The GNU MP Library is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 The GNU MP Library is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15 License for more details.
16 
17 You should have received a copy of the GNU Lesser General Public License
18 along with the GNU MP Library.  If not, see http://www.gnu.org/licenses/.  */
19 
20 #include <stdio.h>
21 #include "gmp.h"
22 #include "gmp-impl.h"
23 
24 
25 /* NTOH_LIMB_FETCH fetches a limb which is in network byte order (ie. big
26    endian) and produces a normal host byte order result. */
27 
28 #if HAVE_LIMB_BIG_ENDIAN
29 #define NTOH_LIMB_FETCH(limb, src)  do { (limb) = *(src); } while (0)
30 #endif
31 
32 #if HAVE_LIMB_LITTLE_ENDIAN
33 #define NTOH_LIMB_FETCH(limb, src)  BSWAP_LIMB_FETCH (limb, src)
34 #endif
35 
36 #ifndef NTOH_LIMB_FETCH
37 #define NTOH_LIMB_FETCH(limb, src)                              \
38   do {                                                          \
39     const unsigned char  *__p = (const unsigned char *) (src);  \
40     mp_limb_t  __limb;                                          \
41     int        __i;                                             \
42     __limb = 0;                                                 \
43     for (__i = 0; __i < BYTES_PER_MP_LIMB; __i++)               \
44       __limb = (__limb << 8) | __p[__i];                        \
45     (limb) = __limb;                                            \
46   } while (0)
47 #endif
48 
49 
50 /* Enhancement: The byte swap loop ought to be safe to vectorize on Cray
51    etc, but someone who knows what they're doing needs to check it.  */
52 
53 size_t
54 mpz_inp_raw (mpz_ptr x, FILE *fp)
55 {
56   unsigned char  csize_bytes[4];
57   mp_size_t      csize, abs_xsize, i;
58   size_t         abs_csize;
59   char           *cp;
60   mp_ptr         xp, sp, ep;
61   mp_limb_t      slimb, elimb;
62 
63   if (fp == 0)
64     fp = stdin;
65 
66   /* 4 bytes for size */
67   if (fread (csize_bytes, sizeof (csize_bytes), 1, fp) != 1)
68     return 0;
69 
70   csize =
71     (  (mp_size_t) csize_bytes[0] << 24)
72     + ((mp_size_t) csize_bytes[1] << 16)
73     + ((mp_size_t) csize_bytes[2] << 8)
74     + ((mp_size_t) csize_bytes[3]);
75 
76   /* Sign extend if necessary.
77      Could write "csize -= ((csize & 0x80000000L) << 1)", but that tickles a
78      bug in gcc 3.0 for powerpc64 on AIX.  */
79   if (sizeof (csize) > 4 && csize & 0x80000000L)
80     csize -= 0x80000000L << 1;
81 
82   abs_csize = ABS (csize);
83 
84   /* round up to a multiple of limbs */
85   abs_xsize = (abs_csize*8 + GMP_NUMB_BITS-1) / GMP_NUMB_BITS;
86 
87   if (abs_xsize != 0)
88     {
89       MPZ_REALLOC (x, abs_xsize);
90       xp = PTR(x);
91 
92       /* Get limb boundaries right in the read, for the benefit of the
93          non-nails case.  */
94       xp[0] = 0;
95       cp = (char *) (xp + abs_xsize) - abs_csize;
96       if (fread (cp, abs_csize, 1, fp) != 1)
97         return 0;
98 
99       if (GMP_NAIL_BITS == 0)
100         {
101           /* Reverse limbs to least significant first, and byte swap.  If
102              abs_xsize is odd then on the last iteration elimb and slimb are
103              the same.  It doesn't seem extra code to handle that case
104              separately, to save an NTOH.  */
105           sp = xp;
106           ep = xp + abs_xsize-1;
107           for (i = 0; i < (abs_xsize+1)/2; i++)
108             {
109               NTOH_LIMB_FETCH (elimb, ep);
110               NTOH_LIMB_FETCH (slimb, sp);
111               *sp++ = elimb;
112               *ep-- = slimb;
113             }
114         }
115       else
116         {
117           /* It ought to be possible to do the transformation in-place, but
118              for now it's easier to use an extra temporary area.  */
119           mp_limb_t  byte, limb;
120           int        bits;
121           mp_size_t  tpos;
122           mp_ptr     tp;
123           TMP_DECL;
124 
125           TMP_MARK;
126           tp = TMP_ALLOC_LIMBS (abs_xsize);
127           limb = 0;
128           bits = 0;
129           tpos = 0;
130           for (i = abs_csize-1; i >= 0; i--)
131             {
132               byte = (unsigned char) cp[i];
133               limb |= (byte << bits);
134               bits += 8;
135               if (bits >= GMP_NUMB_BITS)
136                 {
137                   ASSERT (tpos < abs_xsize);
138                   tp[tpos++] = limb & GMP_NUMB_MASK;
139                   bits -= GMP_NUMB_BITS;
140                   ASSERT (bits < 8);
141                   limb = byte >> (8 - bits);
142                 }
143             }
144           if (bits != 0)
145             {
146               ASSERT (tpos < abs_xsize);
147               tp[tpos++] = limb;
148             }
149           ASSERT (tpos == abs_xsize);
150 
151           MPN_COPY (xp, tp, abs_xsize);
152           TMP_FREE;
153         }
154 
155       /* GMP 1.x mpz_out_raw wrote high zero bytes, strip any high zero
156          limbs resulting from this.  Should be a non-zero value here, but
157          for safety don't assume that. */
158       MPN_NORMALIZE (xp, abs_xsize);
159     }
160 
161   SIZ(x) = (csize >= 0 ? abs_xsize : -abs_xsize);
162   return abs_csize + 4;
163 }
164