xref: /netbsd-src/external/lgpl3/gmp/dist/mpz/export.c (revision 80d9064ac03cbb6a4174695f0d5b237c8766d3d0)
1 /* mpz_export -- create word data from mpz.
2 
3 Copyright 2002, 2003, 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>  /* for NULL */
21 #include "gmp.h"
22 #include "gmp-impl.h"
23 #include "longlong.h"
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 void *
38 mpz_export (void *data, size_t *countp, int order,
39 	    size_t size, int endian, size_t nail, mpz_srcptr z)
40 {
41   mp_size_t      zsize;
42   mp_srcptr      zp;
43   size_t         count, dummy;
44   unsigned long  numb;
45   unsigned       align;
46 
47   ASSERT (order == 1 || order == -1);
48   ASSERT (endian == 1 || endian == 0 || endian == -1);
49   ASSERT (nail <= 8*size);
50   ASSERT (nail <  8*size || SIZ(z) == 0); /* nail < 8*size+(SIZ(z)==0) */
51 
52   if (countp == NULL)
53     countp = &dummy;
54 
55   zsize = SIZ(z);
56   if (zsize == 0)
57     {
58       *countp = 0;
59       return data;
60     }
61 
62   zsize = ABS (zsize);
63   zp = PTR(z);
64   numb = 8*size - nail;
65   MPN_SIZEINBASE_2EXP (count, zp, zsize, numb);
66   *countp = count;
67 
68   if (data == NULL)
69     data = (*__gmp_allocate_func) (count*size);
70 
71   if (endian == 0)
72     endian = HOST_ENDIAN;
73 
74   align = ((char *) data - (char *) NULL) % sizeof (mp_limb_t);
75 
76   if (nail == GMP_NAIL_BITS)
77     {
78       if (size == sizeof (mp_limb_t) && align == 0)
79 	{
80 	  if (order == -1 && endian == HOST_ENDIAN)
81 	    {
82 	      MPN_COPY ((mp_ptr) data, zp, (mp_size_t) count);
83 	      return data;
84 	    }
85 	  if (order == 1 && endian == HOST_ENDIAN)
86 	    {
87 	      MPN_REVERSE ((mp_ptr) data, zp, (mp_size_t) count);
88 	      return data;
89 	    }
90 
91 	  if (order == -1 && endian == -HOST_ENDIAN)
92 	    {
93 	      MPN_BSWAP ((mp_ptr) data, zp, (mp_size_t) count);
94 	      return data;
95 	    }
96 	  if (order == 1 && endian == -HOST_ENDIAN)
97 	    {
98 	      MPN_BSWAP_REVERSE ((mp_ptr) data, zp, (mp_size_t) count);
99 	      return data;
100 	    }
101 	}
102     }
103 
104   {
105     mp_limb_t      limb, wbitsmask;
106     size_t         i, numb;
107     mp_size_t      j, wbytes, woffset;
108     unsigned char  *dp;
109     int            lbits, wbits;
110     mp_srcptr      zend;
111 
112     numb = size * 8 - nail;
113 
114     /* whole bytes per word */
115     wbytes = numb / 8;
116 
117     /* possible partial byte */
118     wbits = numb % 8;
119     wbitsmask = (CNST_LIMB(1) << wbits) - 1;
120 
121     /* offset to get to the next word */
122     woffset = (endian >= 0 ? size : - (mp_size_t) size)
123       + (order < 0 ? size : - (mp_size_t) size);
124 
125     /* least significant byte */
126     dp = (unsigned char *) data
127       + (order >= 0 ? (count-1)*size : 0) + (endian >= 0 ? size-1 : 0);
128 
129 #define EXTRACT(N, MASK)                                \
130     do {                                                \
131       if (lbits >= (N))                                 \
132         {                                               \
133           *dp = limb MASK;                              \
134           limb >>= (N);                                 \
135           lbits -= (N);                                 \
136         }                                               \
137       else                                              \
138         {                                               \
139           mp_limb_t  newlimb;                           \
140           newlimb = (zp == zend ? 0 : *zp++);           \
141           *dp = (limb | (newlimb << lbits)) MASK;       \
142           limb = newlimb >> ((N)-lbits);                \
143           lbits += GMP_NUMB_BITS - (N);                 \
144         }                                               \
145     } while (0)
146 
147     zend = zp + zsize;
148     lbits = 0;
149     limb = 0;
150     for (i = 0; i < count; i++)
151       {
152 	for (j = 0; j < wbytes; j++)
153 	  {
154 	    EXTRACT (8, + 0);
155 	    dp -= endian;
156 	  }
157 	if (wbits != 0)
158 	  {
159 	    EXTRACT (wbits, & wbitsmask);
160 	    dp -= endian;
161 	    j++;
162 	  }
163 	for ( ; j < size; j++)
164 	  {
165 	    *dp = '\0';
166 	    dp -= endian;
167 	  }
168 	dp += woffset;
169       }
170 
171     ASSERT (zp == PTR(z) + ABSIZ(z));
172 
173     /* low byte of word after most significant */
174     ASSERT (dp == (unsigned char *) data
175 	    + (order < 0 ? count*size : - (mp_size_t) size)
176 	    + (endian >= 0 ? (mp_size_t) size - 1 : 0));
177   }
178   return data;
179 }
180