// List primitive operations // // These are registered in mypyc.primitives.list_ops. #include #include "CPy.h" #ifndef Py_TPFLAGS_SEQUENCE #define Py_TPFLAGS_SEQUENCE (1 << 5) #endif PyObject *CPyList_Build(Py_ssize_t len, ...) { Py_ssize_t i; PyObject *res = PyList_New(len); if (res == NULL) { return NULL; } va_list args; va_start(args, len); for (i = 0; i < len; i++) { // Steals the reference PyObject *value = va_arg(args, PyObject *); PyList_SET_ITEM(res, i, value); } va_end(args); return res; } PyObject *CPyList_GetItemUnsafe(PyObject *list, CPyTagged index) { Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); PyObject *result = PyList_GET_ITEM(list, n); Py_INCREF(result); return result; } PyObject *CPyList_GetItemShort(PyObject *list, CPyTagged index) { Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); Py_ssize_t size = PyList_GET_SIZE(list); if (n >= 0) { if (n >= size) { PyErr_SetString(PyExc_IndexError, "list index out of range"); return NULL; } } else { n += size; if (n < 0) { PyErr_SetString(PyExc_IndexError, "list index out of range"); return NULL; } } PyObject *result = PyList_GET_ITEM(list, n); Py_INCREF(result); return result; } PyObject *CPyList_GetItemShortBorrow(PyObject *list, CPyTagged index) { Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); Py_ssize_t size = PyList_GET_SIZE(list); if (n >= 0) { if (n >= size) { PyErr_SetString(PyExc_IndexError, "list index out of range"); return NULL; } } else { n += size; if (n < 0) { PyErr_SetString(PyExc_IndexError, "list index out of range"); return NULL; } } return PyList_GET_ITEM(list, n); } PyObject *CPyList_GetItem(PyObject *list, CPyTagged index) { if (CPyTagged_CheckShort(index)) { Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); Py_ssize_t size = PyList_GET_SIZE(list); if (n >= 0) { if (n >= size) { PyErr_SetString(PyExc_IndexError, "list index out of range"); return NULL; } } else { n += size; if (n < 0) { PyErr_SetString(PyExc_IndexError, "list index out of range"); return NULL; } } PyObject *result = PyList_GET_ITEM(list, n); Py_INCREF(result); return result; } else { PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); return NULL; } } PyObject *CPyList_GetItemBorrow(PyObject *list, CPyTagged index) { if (CPyTagged_CheckShort(index)) { Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); Py_ssize_t size = PyList_GET_SIZE(list); if (n >= 0) { if (n >= size) { PyErr_SetString(PyExc_IndexError, "list index out of range"); return NULL; } } else { n += size; if (n < 0) { PyErr_SetString(PyExc_IndexError, "list index out of range"); return NULL; } } return PyList_GET_ITEM(list, n); } else { PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); return NULL; } } PyObject *CPyList_GetItemInt64(PyObject *list, int64_t index) { size_t size = PyList_GET_SIZE(list); if (likely((uint64_t)index < size)) { PyObject *result = PyList_GET_ITEM(list, index); Py_INCREF(result); return result; } if (index >= 0) { PyErr_SetString(PyExc_IndexError, "list index out of range"); return NULL; } index += size; if (index < 0) { PyErr_SetString(PyExc_IndexError, "list index out of range"); return NULL; } PyObject *result = PyList_GET_ITEM(list, index); Py_INCREF(result); return result; } PyObject *CPyList_GetItemInt64Borrow(PyObject *list, int64_t index) { size_t size = PyList_GET_SIZE(list); if (likely((uint64_t)index < size)) { return PyList_GET_ITEM(list, index); } if (index >= 0) { PyErr_SetString(PyExc_IndexError, "list index out of range"); return NULL; } index += size; if (index < 0) { PyErr_SetString(PyExc_IndexError, "list index out of range"); return NULL; } return PyList_GET_ITEM(list, index); } bool CPyList_SetItem(PyObject *list, CPyTagged index, PyObject *value) { if (CPyTagged_CheckShort(index)) { Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); Py_ssize_t size = PyList_GET_SIZE(list); if (n >= 0) { if (n >= size) { PyErr_SetString(PyExc_IndexError, "list assignment index out of range"); return false; } } else { n += size; if (n < 0) { PyErr_SetString(PyExc_IndexError, "list assignment index out of range"); return false; } } // PyList_SET_ITEM doesn't decref the old element, so we do Py_DECREF(PyList_GET_ITEM(list, n)); // N.B: Steals reference PyList_SET_ITEM(list, n, value); return true; } else { PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); return false; } } bool CPyList_SetItemInt64(PyObject *list, int64_t index, PyObject *value) { size_t size = PyList_GET_SIZE(list); if (unlikely((uint64_t)index >= size)) { if (index > 0) { PyErr_SetString(PyExc_IndexError, "list assignment index out of range"); return false; } index += size; if (index < 0) { PyErr_SetString(PyExc_IndexError, "list assignment index out of range"); return false; } } // PyList_SET_ITEM doesn't decref the old element, so we do Py_DECREF(PyList_GET_ITEM(list, index)); // N.B: Steals reference PyList_SET_ITEM(list, index, value); return true; } // This function should only be used to fill in brand new lists. bool CPyList_SetItemUnsafe(PyObject *list, CPyTagged index, PyObject *value) { if (CPyTagged_CheckShort(index)) { Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); PyList_SET_ITEM(list, n, value); return true; } else { PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); return false; } } PyObject *CPyList_PopLast(PyObject *obj) { // I tried a specalized version of pop_impl for just removing the // last element and it wasn't any faster in microbenchmarks than // the generic one so I ditched it. return list_pop_impl((PyListObject *)obj, -1); } PyObject *CPyList_Pop(PyObject *obj, CPyTagged index) { if (CPyTagged_CheckShort(index)) { Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); return list_pop_impl((PyListObject *)obj, n); } else { PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); return NULL; } } CPyTagged CPyList_Count(PyObject *obj, PyObject *value) { return list_count((PyListObject *)obj, value); } int CPyList_Insert(PyObject *list, CPyTagged index, PyObject *value) { if (CPyTagged_CheckShort(index)) { Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); return PyList_Insert(list, n, value); } // The max range doesn't exactly coincide with ssize_t, but we still // want to keep the error message compatible with CPython. PyErr_SetString(PyExc_OverflowError, CPYTHON_LARGE_INT_ERRMSG); return -1; } PyObject *CPyList_Extend(PyObject *o1, PyObject *o2) { return _PyList_Extend((PyListObject *)o1, o2); } // Return -2 or error, -1 if not found, or index of first match otherwise. static Py_ssize_t _CPyList_Find(PyObject *list, PyObject *obj) { Py_ssize_t i; for (i = 0; i < Py_SIZE(list); i++) { PyObject *item = PyList_GET_ITEM(list, i); Py_INCREF(item); int cmp = PyObject_RichCompareBool(item, obj, Py_EQ); Py_DECREF(item); if (cmp != 0) { if (cmp > 0) { return i; } else { return -2; } } } return -1; } int CPyList_Remove(PyObject *list, PyObject *obj) { Py_ssize_t index = _CPyList_Find(list, obj); if (index == -2) { return -1; } if (index == -1) { PyErr_SetString(PyExc_ValueError, "list.remove(x): x not in list"); return -1; } return PyList_SetSlice(list, index, index + 1, NULL); } CPyTagged CPyList_Index(PyObject *list, PyObject *obj) { Py_ssize_t index = _CPyList_Find(list, obj); if (index == -2) { return CPY_INT_TAG; } if (index == -1) { PyErr_SetString(PyExc_ValueError, "value is not in list"); return CPY_INT_TAG; } return index << 1; } PyObject *CPySequence_Multiply(PyObject *seq, CPyTagged t_size) { Py_ssize_t size = CPyTagged_AsSsize_t(t_size); if (size == -1 && PyErr_Occurred()) { return NULL; } return PySequence_Repeat(seq, size); } PyObject *CPySequence_RMultiply(CPyTagged t_size, PyObject *seq) { return CPySequence_Multiply(seq, t_size); } PyObject *CPyList_GetSlice(PyObject *obj, CPyTagged start, CPyTagged end) { if (likely(PyList_CheckExact(obj) && CPyTagged_CheckShort(start) && CPyTagged_CheckShort(end))) { Py_ssize_t startn = CPyTagged_ShortAsSsize_t(start); Py_ssize_t endn = CPyTagged_ShortAsSsize_t(end); if (startn < 0) { startn += PyList_GET_SIZE(obj); } if (endn < 0) { endn += PyList_GET_SIZE(obj); } return PyList_GetSlice(obj, startn, endn); } return CPyObject_GetSlice(obj, start, end); } int CPySequence_Check(PyObject *obj) { return Py_TYPE(obj)->tp_flags & Py_TPFLAGS_SEQUENCE; }