xref: /netbsd-src/external/lgpl3/gmp/dist/mpz/import.c (revision 8585484ef87f5a04d32332313cdb799625f4faf8)
1 /* mpz_import -- set mpz from word data.
2 
3 Copyright 2002, 2012 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 
26 #if HAVE_LIMB_BIG_ENDIAN
27 #define HOST_ENDIAN     1
28 #endif
29 #if HAVE_LIMB_LITTLE_ENDIAN
30 #define HOST_ENDIAN     (-1)
31 #endif
32 #ifndef HOST_ENDIAN
33 static const mp_limb_t  endian_test = (CNST_LIMB(1) << (GMP_LIMB_BITS-7)) - 1;
34 #define HOST_ENDIAN     (* (signed char *) &endian_test)
35 #endif
36 
37 
38 void
39 mpz_import (mpz_ptr z, size_t count, int order,
40 	    size_t size, int endian, size_t nail, const void *data)
41 {
42   mp_size_t  zsize;
43   mp_ptr     zp;
44 
45   ASSERT (order == 1 || order == -1);
46   ASSERT (endian == 1 || endian == 0 || endian == -1);
47   ASSERT (nail <= 8*size);
48 
49   zsize = (count * (8*size - nail) + GMP_NUMB_BITS-1) / GMP_NUMB_BITS;
50   zp = MPZ_REALLOC (z, zsize);
51 
52   if (endian == 0)
53     endian = HOST_ENDIAN;
54 
55   /* Can't use these special cases with nails currently, since they don't
56      mask out the nail bits in the input data.  */
57   if (nail == 0 && GMP_NAIL_BITS == 0)
58     {
59       unsigned  align = ((char *) data - (char *) NULL) % sizeof (mp_limb_t);
60 
61       if (order == -1
62 	  && size == sizeof (mp_limb_t)
63 	  && endian == HOST_ENDIAN
64 	  && align == 0)
65 	{
66 	  MPN_COPY (zp, (mp_srcptr) data, (mp_size_t) count);
67 	  goto done;
68 	}
69 
70       if (order == -1
71 	  && size == sizeof (mp_limb_t)
72 	  && endian == - HOST_ENDIAN
73 	  && align == 0)
74 	{
75 	  MPN_BSWAP (zp, (mp_srcptr) data, (mp_size_t) count);
76 	  goto done;
77 	}
78 
79       if (order == 1
80 	  && size == sizeof (mp_limb_t)
81 	  && endian == HOST_ENDIAN
82 	  && align == 0)
83 	{
84 	  MPN_REVERSE (zp, (mp_srcptr) data, (mp_size_t) count);
85 	  goto done;
86 	}
87     }
88 
89   {
90     mp_limb_t      limb, byte, wbitsmask;
91     size_t         i, j, numb, wbytes;
92     mp_size_t      woffset;
93     unsigned char  *dp;
94     int            lbits, wbits;
95 
96     numb = size * 8 - nail;
97 
98     /* whole bytes to process */
99     wbytes = numb / 8;
100 
101     /* partial byte to process */
102     wbits = numb % 8;
103     wbitsmask = (CNST_LIMB(1) << wbits) - 1;
104 
105     /* offset to get to the next word after processing wbytes and wbits */
106     woffset = (numb + 7) / 8;
107     woffset = (endian >= 0 ? woffset : -woffset)
108       + (order < 0 ? size : - (mp_size_t) size);
109 
110     /* least significant byte */
111     dp = (unsigned char *) data
112       + (order >= 0 ? (count-1)*size : 0) + (endian >= 0 ? size-1 : 0);
113 
114 #define ACCUMULATE(N)                                   \
115     do {                                                \
116       ASSERT (lbits < GMP_NUMB_BITS);                   \
117       ASSERT (limb <= (CNST_LIMB(1) << lbits) - 1);     \
118                                                         \
119       limb |= (mp_limb_t) byte << lbits;                \
120       lbits += (N);                                     \
121       if (lbits >= GMP_NUMB_BITS)                       \
122         {                                               \
123           *zp++ = limb & GMP_NUMB_MASK;                 \
124           lbits -= GMP_NUMB_BITS;                       \
125           ASSERT (lbits < (N));                         \
126           limb = byte >> ((N) - lbits);                 \
127         }                                               \
128     } while (0)
129 
130     limb = 0;
131     lbits = 0;
132     for (i = 0; i < count; i++)
133       {
134 	for (j = 0; j < wbytes; j++)
135 	  {
136 	    byte = *dp;
137 	    dp -= endian;
138 	    ACCUMULATE (8);
139 	  }
140 	if (wbits != 0)
141 	  {
142 	    byte = *dp & wbitsmask;
143 	    dp -= endian;
144 	    ACCUMULATE (wbits);
145 	  }
146 	dp += woffset;
147       }
148 
149     if (lbits != 0)
150       {
151 	ASSERT (lbits <= GMP_NUMB_BITS);
152 	ASSERT_LIMB (limb);
153 	*zp++ = limb;
154       }
155 
156     ASSERT (zp == PTR(z) + zsize);
157 
158     /* low byte of word after most significant */
159     ASSERT (dp == (unsigned char *) data
160 	    + (order < 0 ? count*size : - (mp_size_t) size)
161 	    + (endian >= 0 ? (mp_size_t) size - 1 : 0));
162 
163   }
164 
165  done:
166   zp = PTR(z);
167   MPN_NORMALIZE (zp, zsize);
168   SIZ(z) = zsize;
169 }
170