Skip to content

Add public function PyLong_GetDigits() #31

@skirpichev

Description

@skirpichev

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions