/* -*- coding: utf-8 -*- */
/*
 * Some Python helper for C.
 *
 * Also contains some interesting backports from later Python versions.
 *
 * :Copyright: (c) 2021-2022, Franz Glasner. All rights reserved.
 * :License:   BSD-3-Clause. See LICENSE.txt for details.
 */

#if !defined(_PY_HELPER_H_d9df407295df4884a88e56699f6c6d8d)
#define _PY_HELPER_H_d9df407295df4884a88e56699f6c6d8d

#if PY_VERSION_HEX < 0x030A0000

/*
 * Return a new owned reference to an object
 */
static inline
PyObject *
Py_NewRef(PyObject *obj)
{
    Py_INCREF(obj);
    return obj;
}


/*
 * Return a new owned reference to an object when the input can be NULL
 */
static inline
PyObject *
Py_XNewRef(PyObject *obj)
{
    Py_XINCREF(obj);
    return obj;
}

#endif /* PY_VERSION_HEX < 0x030A0000 */


static inline
void
py_clear_ref(PyObject **obj)
{
    PyObject *tmp;

    if ((tmp = *obj) != NULL) {
        *obj = NULL;
        Py_DECREF(tmp);
    }
}


/*
 * NOTE: This implementation is valid for CPython only!
 */
static inline
int
py_object_is(PyObject *obj1, PyObject *obj2)
{
    return (obj1 == obj2);
}


/*
 * NOTE: This implementation is valid for CPython only!
 */
static inline
int
py_object_isnot(PyObject *obj1, PyObject *obj2)
{
    return (obj1 != obj2);
}


/**
 * Copy from source to destination and make an owned reference.
 * Also safely clear the destination before.
 */
static inline
void
py_assign(PyObject **dest, PyObject *src)
{
    Py_DECREF(*dest);
    *dest = Py_NewRef(src);
}


/**
 * Copy from source to destination and make an owned reference.
 * Also clear the destination before.
 */
static inline
void
py_x_assign(PyObject **dest, PyObject *src)
{
    Py_XDECREF(*dest);
    *dest = Py_NewRef(src);
}


/**
 * Copy from source to destination and make an owned reference.
 * Also clear the destination before. The source object may be NULL.
 */
static inline
void
py_assign_x(PyObject **dest, PyObject *src)
{
    Py_DECREF(*dest);
    *dest = Py_XNewRef(src);
}


/**
 * Copy from source to destination and make an owned reference.
 * Also safely clear the destination before. The source object may be NULL.
 */
static inline
void
py_x_assign_x(PyObject **dest, PyObject *src)
{
    Py_XDECREF(*dest);
    *dest = Py_XNewRef(src);
}


/*
 * Transfer from a borrowed reference to an owned one and clear the source.
 * Also clear the destination before.
 */
static inline
void
py_transfer_borrowed(PyObject **dest, PyObject **src)
{
    Py_DECREF(*dest);
    *dest = Py_NewRef(*src);
    *src = NULL;
}


/*
 * Transfer from a borrowed reference to an owned one and clear the source.
 * Also safely clear the destination before.
 */
static inline
void
py_x_transfer_borrowed(PyObject **dest, PyObject **src)
{
    Py_XDECREF(*dest);
    *dest = Py_NewRef(*src);
    *src = NULL;
}


/*
 * Transfer from a borrowed reference to an owned one and clear the source.
 * Also clear the destination before. The source object may be NULL.
 */
static inline
void
py_transfer_x_borrowed(PyObject **dest, PyObject **src)
{
    Py_DECREF(*dest);
    *dest = Py_XNewRef(*src);
    *src = NULL;
}


/*
 * Transfer from a borrowed reference to an owned one and clear the source.
 * Also safely clear the destination before. The source object may be NULL.
 */
static inline
void
py_x_transfer_x_borrowed(PyObject **dest, PyObject **src)
{
    Py_XDECREF(*dest);
    *dest = Py_XNewRef(*src);
    *src = NULL;
}


/*
 * Transfer ownership from a owned reference to an owned one and clear the
 * source.
 * Also clear the destination before.
 */
static inline
void
py_transfer_owned(PyObject **dest, PyObject **src)
{
    Py_DECREF(*dest);
    *dest = *src;
    *src = NULL;
}


/*
 * Transfer ownership from a owned reference to an owned one and clear the
 * source.
 * Also safely clear the destination before.
 */
static inline
void
py_x_transfer_owned(PyObject **dest, PyObject **src)
{
    Py_XDECREF(*dest);
    *dest = *src;
    *src = NULL;
}
   

#endif
