/ src / gmpy2_convert_utils.c
gmpy2_convert_utils.c
  1  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2   * gmpy2_convert_utils.c                                                   *
  3   * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  4   * Python interface to the GMP or MPIR, MPFR, and MPC multiple precision   *
  5   * libraries.                                                              *
  6   *                                                                         *
  7   * Copyright 2000 - 2009 Alex Martelli                                     *
  8   *                                                                         *
  9   * Copyright 2008 - 2021 Case Van Horsen                                   *
 10   *                                                                         *
 11   * This file is part of GMPY2.                                             *
 12   *                                                                         *
 13   * GMPY2 is free software: you can redistribute it and/or modify it under  *
 14   * the terms of the GNU Lesser General Public License as published by the  *
 15   * Free Software Foundation, either version 3 of the License, or (at your  *
 16   * option) any later version.                                              *
 17   *                                                                         *
 18   * GMPY2 is distributed in the hope that it will be useful, but WITHOUT    *
 19   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or   *
 20   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public    *
 21   * License for more details.                                               *
 22   *                                                                         *
 23   * You should have received a copy of the GNU Lesser General Public        *
 24   * License along with GMPY2; if not, see <http://www.gnu.org/licenses/>    *
 25   * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 26  
 27  /* ======================================================================== *
 28   * Conversion between Integer objects and C types.                          *
 29   * ======================================================================== *
 30   *
 31   * Optimized routines for converting an Integer object (Python's integer
 32   * type(s), mpz, plus types defining __mpz__) to various C types.
 33   *
 34   * Note: These functions will not set any exceptions!
 35   *
 36   */
 37  
 38  #include "longintrepr.h"
 39  
 40  #define PY_ABS_LONG_MIN     (0-(unsigned long)LONG_MIN)
 41  
 42  /* The various ...AndError functions indicate exceptions as follows:
 43   *   1) If error is -1, then the argument is negative and the result could
 44   *      not fit into the desired return type.
 45   *   2) If error is 1, then the argument is positive and the result could
 46   *      not fit into the desired return type.
 47   *   3) If error is 2, then the argument is not a recognized type.
 48   *   4) If error is 0, then a valid result has been returned.
 49   *
 50   * The functions special-case the most common paths and then call the
 51   * corresponding Python functions to support the general case.
 52   *
 53   */
 54  
 55  static long
 56  GMPy_Integer_AsLongAndError(PyObject *obj, int *error)
 57  {
 58      register PyLongObject *v;
 59      MPZ_Object *temp_mpz = NULL;
 60      unsigned long x, prev;
 61      long res = 0;
 62      Py_ssize_t i;
 63      int sign;
 64  
 65      *error = 0;
 66  
 67  #ifdef PY2
 68      if (PyInt_Check(obj)) {
 69          return PyInt_AS_LONG(obj);
 70      }
 71  #endif
 72  
 73      if (PyLong_Check(obj)) {
 74          v = (PyLongObject *)obj;
 75          i = Py_SIZE(v);
 76  
 77          switch (i) {
 78          case -1:
 79              res = -(sdigit)v->ob_digit[0];
 80              break;
 81          case 0:
 82              break;
 83          case 1:
 84              res = v->ob_digit[0];
 85              break;
 86          default:
 87              sign = 1;
 88              x = 0;
 89              if (i < 0) {
 90                  sign = -1;
 91                  i = -(i);
 92              }
 93              while (--i >= 0) {
 94                  prev = x;
 95                  x = (x << PyLong_SHIFT) + v->ob_digit[i];
 96                  if ((x >> PyLong_SHIFT) != prev) {
 97                      *error = sign;
 98                      return res;
 99                  }
100              }
101              /* Haven't lost any bits, but casting to long requires extra care.
102               */
103              if (x <= (unsigned long)LONG_MAX) {
104                  res = (long)x * sign;
105              }
106              else if (sign < 0 && x == PY_ABS_LONG_MIN) {
107                  res = LONG_MIN;
108              }
109              else {
110                  *error = sign;
111              }
112          }
113          return res;
114      }
115  
116      if (CHECK_MPZANY(obj)) {
117          if (mpz_fits_slong_p(MPZ(obj))) {
118              res = (long) mpz_get_si(MPZ(obj));
119          }
120          else {
121              *error = mpz_sgn(MPZ(obj));
122              res = 0;
123          }
124          return res;
125      }
126  
127      if (HAS_STRICT_MPZ_CONVERSION(obj)) {
128          temp_mpz = (MPZ_Object *) PyObject_CallMethod(obj, "__mpz__", NULL);
129  
130          if (temp_mpz != NULL && MPZ_Check(temp_mpz)) {
131              if (mpz_fits_slong_p(MPZ(temp_mpz))) {
132                  res = (long) mpz_get_si(MPZ(temp_mpz));
133              }
134              else {
135                  *error = mpz_sgn(MPZ(temp_mpz));
136                  res = 0;
137              }
138          }
139          Py_XDECREF((PyObject*)temp_mpz);
140          return res;
141      }
142  
143      *error = 2;
144      return 0;
145      /* Fall back to using PyLong_AsLongAndOverFlow. This allows gmpy2 to
146       * follow the same semantics as Python for conversion.
147       */
148  
149  /*    res = PyLong_AsLongAndOverflow(obj, error);
150  
151      if ((res == -1) && PyErr_Occurred()) {
152          PyErr_Clear();
153          *error = 2;
154          return 0;
155      }
156      else {
157          if (*error) {
158              return 0;
159          }
160          else {
161              return res;
162          }
163      }
164  */
165  }
166  
167  static unsigned long
168  GMPy_Integer_AsUnsignedLongAndError(PyObject *obj, int *error)
169  {
170      register PyLongObject *v;
171      MPZ_Object *temp_mpz = NULL;
172      unsigned long x, prev;
173      unsigned long res = 0;
174      Py_ssize_t i;
175  
176      *error = 0;
177  
178  #ifdef PY2
179      if (PyInt_Check(obj)) {
180          long temp = PyInt_AS_LONG(obj);
181          if (temp < 0) {
182              *error = -1;
183              return 0;
184          }
185          else {
186              return (unsigned long)temp;
187          }
188      }
189  #endif
190  
191      if (PyLong_Check(obj)) {
192          v = (PyLongObject *)obj;
193          i = Py_SIZE(v);
194  
195          if (i < 0) {
196              *error = -1;
197              return res;
198          }
199  
200          switch (i) {
201          case 0:
202              break;
203          case 1:
204              res = v->ob_digit[0];
205              break;
206          default:
207              x = 0;
208              while (--i >= 0) {
209                  prev = x;
210                  x = (x << PyLong_SHIFT) + v->ob_digit[i];
211                  if ((x >> PyLong_SHIFT) != prev) {
212                      *error = 1;
213                      return res;
214                  }
215              }
216              res = x;
217          }
218          return res;
219      }
220  
221      if (CHECK_MPZANY(obj)) {
222          if (mpz_fits_ulong_p(MPZ(obj))) {
223              res = (unsigned long) mpz_get_ui(MPZ(obj));
224          }
225          else {
226              *error = mpz_sgn(MPZ(obj));
227              res = 0;
228          }
229          return res;
230      }
231  
232      if (HAS_STRICT_MPZ_CONVERSION(obj)) {
233          temp_mpz = (MPZ_Object *) PyObject_CallMethod(obj, "__mpz__", NULL);
234  
235          if (temp_mpz != NULL && MPZ_Check(temp_mpz)) {
236              if (mpz_fits_ulong_p(MPZ(temp_mpz))) {
237                  res = (unsigned long) mpz_get_ui(MPZ(temp_mpz));
238              }
239              else {
240                  *error = mpz_sgn(MPZ(temp_mpz));
241                  res = 0;
242              }
243          }
244          Py_XDECREF((PyObject*)temp_mpz);
245          return res;
246      }
247  
248      *error = 2;
249      return 0;
250  }
251  
252  static long
253  c_long_From_Integer(PyObject *obj)
254  {
255      long result;
256      int error;
257  
258      result = GMPy_Integer_AsLongAndError(obj, &error);
259      if (!error) {
260          return result;
261      }
262      else {
263          if (error == 2) {
264              TYPE_ERROR("could not convert object to integer");
265          }
266          else {
267              OVERFLOW_ERROR("value too large to convert to C long");
268          }
269          return -1;
270      }
271  }
272  
273  static unsigned long
274  c_ulong_From_Integer(PyObject *obj)
275  {
276      unsigned long result;
277      int error;
278  
279      result = GMPy_Integer_AsUnsignedLongAndError(obj, &error);
280      if (!error) {
281          return result;
282      }
283      else {
284          if (error == 2) {
285              TYPE_ERROR("could not convert object to integer");
286          }
287          else if (error == 1) {
288              OVERFLOW_ERROR("value too large to convert to C unsigned long");
289          }
290          else if (error < 0) {
291              VALUE_ERROR("a non-negative value is required");
292          }
293          return (unsigned long)(-1);
294      }
295  }
296  
297  /* The follow code is only used on Windows x64 platform. We check for _WIN64
298   * but we then assume the PY_LONG_LONG is defined.
299   */
300  
301  #ifdef _WIN64
302  
303  #define PY_ABS_LLONG_MIN (0-(unsigned PY_LONG_LONG)PY_LLONG_MIN)
304  
305  static PY_LONG_LONG
306  GMPy_Integer_AsLongLongAndError(PyObject *obj, int *error)
307  {
308      register PyLongObject *v;
309      MPZ_Object *temp_mpz = NULL;
310      unsigned PY_LONG_LONG x, prev;
311      PY_LONG_LONG res = 0;
312      Py_ssize_t i;
313      int sign;
314  
315      *error = 0;
316  
317  #ifdef PY2
318      if (PyInt_Check(obj)) {
319          return (PY_LONG_LONG)PyInt_AS_LONG(obj);
320      }
321  #endif
322  
323      if (PyLong_Check(obj)) {
324          v = (PyLongObject *)obj;
325          i = Py_SIZE(v);
326  
327          switch (i) {
328          case -1:
329              res = -(sdigit)v->ob_digit[0];
330              break;
331          case 0:
332              break;
333          case 1:
334              res = v->ob_digit[0];
335              break;
336          default:
337              sign = 1;
338              x = 0;
339              if (i < 0) {
340                  sign = -1;
341                  i = -(i);
342              }
343              while (--i >= 0) {
344                  prev = x;
345                  x = (x << PyLong_SHIFT) + v->ob_digit[i];
346                  if ((x >> PyLong_SHIFT) != prev) {
347                      *error = sign;
348                      return res;
349                  }
350              }
351              /* Haven't lost any bits, but casting to long requires extra care.
352               */
353              if (x <= (unsigned PY_LONG_LONG)PY_LLONG_MAX) {
354                  res = (PY_LONG_LONG)x * sign;
355              }
356              else if (sign < 0 && x == PY_ABS_LLONG_MIN) {
357                  res = PY_LLONG_MIN;
358              }
359              else {
360                  *error = sign;
361              }
362          }
363          return res;
364      }
365  
366      if (CHECK_MPZANY(obj)) {
367          sign = mpz_sgn(MPZ(obj));
368          if (sign) {
369              if (mpz_sizeinbase(MPZ(obj), 256) <= sizeof(x)) {
370                  x = 0;
371                  mpz_export(&x, NULL, 1, sizeof(x), 0, 0, MPZ(obj));
372              }
373              if (x <= (unsigned PY_LONG_LONG)PY_LLONG_MAX) {
374                  res = (PY_LONG_LONG)x * sign;
375              }
376              else if (sign < 0 && x == PY_ABS_LLONG_MIN) {
377                  res = PY_LLONG_MIN;
378              }
379              else {
380                  *error = sign;
381              }
382          }
383          return res;
384      }
385  
386      if (HAS_STRICT_MPZ_CONVERSION(obj)) {
387          temp_mpz = (MPZ_Object *) PyObject_CallMethod(obj, "__mpz__", NULL);
388  
389          if (temp_mpz != NULL && MPZ_Check(temp_mpz)) {
390              sign = mpz_sgn(MPZ(obj));
391              if (sign) {
392                  if (mpz_sizeinbase(MPZ(obj), 256) <= sizeof(x)) {
393                      x = 0;
394                      mpz_export(&x, NULL, 1, sizeof(x), 0, 0, MPZ(obj));
395                  }
396                  if (x <= (unsigned PY_LONG_LONG)PY_LLONG_MAX) {
397                      res = (PY_LONG_LONG)x * sign;
398                  }
399                  else if (sign < 0 && x == PY_ABS_LLONG_MIN) {
400                      res = PY_LLONG_MIN;
401                  }
402                  else {
403                      *error = sign;
404                  }
405              }
406              Py_XDECREF((PyObject*)temp_mpz);
407          }
408          return res;
409      }
410  
411      *error = 2;
412      return 0;
413  }
414  
415  static unsigned PY_LONG_LONG
416  GMPy_Integer_AsUnsignedLongLongAndError(PyObject *obj, int *error)
417  {
418      register PyLongObject *v;
419      MPZ_Object *temp_mpz = NULL;
420      unsigned PY_LONG_LONG x, prev, res = 0;
421      Py_ssize_t i;
422      int sign;
423  
424      *error = 0;
425  
426  #ifdef PY2
427      if (PyInt_Check(obj)) {
428          long temp = PyInt_AS_LONG(obj);
429          if (temp < 0) {
430              *error = -1;
431              return res;
432          }
433          else {
434              return (unsigned PY_LONG_LONG)temp;
435          }
436      }
437  #endif
438  
439      if (PyLong_Check(obj)) {
440          v = (PyLongObject *)obj;
441          i = Py_SIZE(v);
442  
443          if (i < 0) {
444              *error = -1;
445              return res;
446          }
447  
448          switch (i) {
449          case 0:
450              break;
451          case 1:
452              res = v->ob_digit[0];
453              break;
454          default:
455              x = 0;
456              while (--i >= 0) {
457                  prev = x;
458                  x = (x << PyLong_SHIFT) + v->ob_digit[i];
459                  if ((x >> PyLong_SHIFT) != prev) {
460                      *error = 1;
461                      return res;
462                  }
463              }
464              res = x;
465          }
466          return res;
467      }
468  
469      if (CHECK_MPZANY(obj)) {
470          sign = mpz_sgn(MPZ(obj));
471          if (sign < 0) {
472              *error = -1;
473              return res;
474          }
475          else if (sign) {
476              if (mpz_sizeinbase(MPZ(obj), 256) <= sizeof(res)) {
477                  mpz_export(&res, NULL, 1, sizeof(x), 0, 0, MPZ(obj));
478              }
479              else {
480                  *error = 1;
481              }
482          }
483          return res;
484      }
485  
486      if (HAS_STRICT_MPZ_CONVERSION(obj)) {
487          temp_mpz = (MPZ_Object *) PyObject_CallMethod(obj, "__mpz__", NULL);
488  
489          if (temp_mpz != NULL && MPZ_Check(temp_mpz)) {
490              sign = mpz_sgn(MPZ(obj));
491              if (sign < 0) {
492                  *error = -1;
493              }
494              else if (sign) {
495                  if (mpz_sizeinbase(MPZ(obj), 256) <= sizeof(res)) {
496                      mpz_export(&res, NULL, 1, sizeof(x), 0, 0, MPZ(obj));
497                  }
498              }
499              else {
500                  *error = 1;
501              }
502              Py_XDECREF((PyObject*)temp_mpz);
503          }
504          return res;
505      }
506  
507      *error = 2;
508      return 0;
509  }
510  
511  static  PY_LONG_LONG
512  c_longlong_From_Integer(PyObject *obj)
513  {
514      PY_LONG_LONG result;
515      int error;
516  
517      result = GMPy_Integer_AsLongLongAndError(obj, &error);
518      if (!error) {
519          return result;
520      }
521      else {
522          if (error == 2) {
523              TYPE_ERROR("could not convert object to integer");
524          }
525          else {
526              OVERFLOW_ERROR("value too large to convert to C long long");
527          }
528          return -1;
529      }
530  }
531  
532  static unsigned PY_LONG_LONG
533  c_ulonglong_From_Integer(PyObject *obj)
534  {
535      unsigned PY_LONG_LONG result;
536      int error;
537  
538      result = GMPy_Integer_AsUnsignedLongLongAndError(obj, &error);
539      if (!error) {
540          return result;
541      }
542      else {
543          if (error == 2) {
544              TYPE_ERROR("could not convert object to integer");
545          }
546          else if (error == 1) {
547              OVERFLOW_ERROR("value too large to convert to C unsigned long long");
548          }
549          else if (error < 0) {
550              VALUE_ERROR("a non-negative value is required");
551          }
552          return (unsigned PY_LONG_LONG)(-1);
553      }
554  }
555  
556  #endif
557