skirpichev · GitHub

That will allow fast import of CPython integers if external multiple precision library support mpz_import()-like interface (e.g. LibTomMath has mp_unpack()). Currently gmpy2 (see this or Sage (see this) use private API to do this. Using new PyLong_FromNativeBytes() / PyLong_AsNativeBytes() will be a performance regression for such projects.

Proposed interface:

/* Access the integer value as an array of digits.
   On success, return array of digits and set *ndigits to number of digits.
   On failure, return NULL with an exception set.
   This function always succeeds if obj is a PyLongObject or its subtype.
 */
const digits* PyLong_GetDigits(PyObject* obj, Py_ssize_t *ndigits);

API usage:

static void
mpz_set_PyLong(mpz_t z, PyObject *obj)
{
    Py_ssize_t len;
    const digit *digits = PyLong_GetDigits(obj, &len);
    switch (len) {
    case 1:
        mpz_set_si(z, (sdigit)digits[0]);
        break;
    case 0:
        mpz_set_si(z, 0);
        break;
    default:
        mpz_import(z, len, -1, sizeof(digit), 0,
                   sizeof(digit)*8 - PYLONG_BITS_IN_DIGIT, digits);
    }
    int sign = 1;
    PyLong_GetSign(obj, &sign);
    if (sign < 0) {
        mpz_neg(z, z);
    }
    return;
}

Above interface resembles combined GMP's functions mpz_size() and mpz_limbs_read(). It might worth to consider also export from external multiple precision libraries, that offer mpz_export()-like API. gmpy2 does this using private API to access digits/digit count and also the _PyLong_New() to allocate an integer of appropriate size.

So, alternative interface to support both reading and writing might look like this:

/* Return number of digits or -1 on failure.  Shouldn't fail on int's. */
Py_ssize_t PyLong_DigitCount(PyObject *obj);
/* Return array of digits or NULL on failure. Shouldn't fail on int's. */
digit* PyLong_GetDigits(PyObject *obj);
/* Return a new integer object with unspecified absolute value of given
   size (in digits) and with a given sign.  On failure return NULL */
PyObject* PyLong_New(const Py_ssize_t ndigits, const int sign);

The PyLong_New() shouldn't violate #56: freshly created integer will be a correct one, just with an unspecified absolute value.

Read the original on github.com ↗