Imported Upstream version 6.0.0
[platform/upstream/gmp.git] / mpz / import.c
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 either:
9
10   * the GNU Lesser General Public License as published by the Free
11     Software Foundation; either version 3 of the License, or (at your
12     option) any later version.
13
14 or
15
16   * the GNU General Public License as published by the Free Software
17     Foundation; either version 2 of the License, or (at your option) any
18     later version.
19
20 or both in parallel, as here.
21
22 The GNU MP Library is distributed in the hope that it will be useful, but
23 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
25 for more details.
26
27 You should have received copies of the GNU General Public License and the
28 GNU Lesser General Public License along with the GNU MP Library.  If not,
29 see https://www.gnu.org/licenses/.  */
30
31 #include <stdio.h>
32 #include "gmp.h"
33 #include "gmp-impl.h"
34
35
36
37 #if HAVE_LIMB_BIG_ENDIAN
38 #define HOST_ENDIAN     1
39 #endif
40 #if HAVE_LIMB_LITTLE_ENDIAN
41 #define HOST_ENDIAN     (-1)
42 #endif
43 #ifndef HOST_ENDIAN
44 static const mp_limb_t  endian_test = (CNST_LIMB(1) << (GMP_LIMB_BITS-7)) - 1;
45 #define HOST_ENDIAN     (* (signed char *) &endian_test)
46 #endif
47
48
49 void
50 mpz_import (mpz_ptr z, size_t count, int order,
51             size_t size, int endian, size_t nail, const void *data)
52 {
53   mp_size_t  zsize;
54   mp_ptr     zp;
55
56   ASSERT (order == 1 || order == -1);
57   ASSERT (endian == 1 || endian == 0 || endian == -1);
58   ASSERT (nail <= 8*size);
59
60   zsize = BITS_TO_LIMBS (count * (8*size - nail));
61   zp = MPZ_NEWALLOC (z, zsize);
62
63   if (endian == 0)
64     endian = HOST_ENDIAN;
65
66   /* Can't use these special cases with nails currently, since they don't
67      mask out the nail bits in the input data.  */
68   if (nail == 0 && GMP_NAIL_BITS == 0)
69     {
70       unsigned  align = ((char *) data - (char *) NULL) % sizeof (mp_limb_t);
71
72       if (order == -1
73           && size == sizeof (mp_limb_t)
74           && endian == HOST_ENDIAN
75           && align == 0)
76         {
77           MPN_COPY (zp, (mp_srcptr) data, (mp_size_t) count);
78           goto done;
79         }
80
81       if (order == -1
82           && size == sizeof (mp_limb_t)
83           && endian == - HOST_ENDIAN
84           && align == 0)
85         {
86           MPN_BSWAP (zp, (mp_srcptr) data, (mp_size_t) count);
87           goto done;
88         }
89
90       if (order == 1
91           && size == sizeof (mp_limb_t)
92           && endian == HOST_ENDIAN
93           && align == 0)
94         {
95           MPN_REVERSE (zp, (mp_srcptr) data, (mp_size_t) count);
96           goto done;
97         }
98     }
99
100   {
101     mp_limb_t      limb, byte, wbitsmask;
102     size_t         i, j, numb, wbytes;
103     mp_size_t      woffset;
104     unsigned char  *dp;
105     int            lbits, wbits;
106
107     numb = size * 8 - nail;
108
109     /* whole bytes to process */
110     wbytes = numb / 8;
111
112     /* partial byte to process */
113     wbits = numb % 8;
114     wbitsmask = (CNST_LIMB(1) << wbits) - 1;
115
116     /* offset to get to the next word after processing wbytes and wbits */
117     woffset = (numb + 7) / 8;
118     woffset = (endian >= 0 ? woffset : -woffset)
119       + (order < 0 ? size : - (mp_size_t) size);
120
121     /* least significant byte */
122     dp = (unsigned char *) data
123       + (order >= 0 ? (count-1)*size : 0) + (endian >= 0 ? size-1 : 0);
124
125 #define ACCUMULATE(N)                                   \
126     do {                                                \
127       ASSERT (lbits < GMP_NUMB_BITS);                   \
128       ASSERT (limb <= (CNST_LIMB(1) << lbits) - 1);     \
129                                                         \
130       limb |= (mp_limb_t) byte << lbits;                \
131       lbits += (N);                                     \
132       if (lbits >= GMP_NUMB_BITS)                       \
133         {                                               \
134           *zp++ = limb & GMP_NUMB_MASK;                 \
135           lbits -= GMP_NUMB_BITS;                       \
136           ASSERT (lbits < (N));                         \
137           limb = byte >> ((N) - lbits);                 \
138         }                                               \
139     } while (0)
140
141     limb = 0;
142     lbits = 0;
143     for (i = 0; i < count; i++)
144       {
145         for (j = 0; j < wbytes; j++)
146           {
147             byte = *dp;
148             dp -= endian;
149             ACCUMULATE (8);
150           }
151         if (wbits != 0)
152           {
153             byte = *dp & wbitsmask;
154             dp -= endian;
155             ACCUMULATE (wbits);
156           }
157         dp += woffset;
158       }
159
160     if (lbits != 0)
161       {
162         ASSERT (lbits <= GMP_NUMB_BITS);
163         ASSERT_LIMB (limb);
164         *zp++ = limb;
165       }
166
167     ASSERT (zp == PTR(z) + zsize);
168
169     /* low byte of word after most significant */
170     ASSERT (dp == (unsigned char *) data
171             + (order < 0 ? count*size : - (mp_size_t) size)
172             + (endian >= 0 ? (mp_size_t) size - 1 : 0));
173
174   }
175
176  done:
177   zp = PTR(z);
178   MPN_NORMALIZE (zp, zsize);
179   SIZ(z) = zsize;
180 }