/* Big parts of this file are copied (with modifications) from
CPython/Objects/tupleobject.c.
Portions Copyright (c) PSF (and other CPython copyright holders).
Portions Copyright (c) 2016-present MagicStack Inc.
License: PSFL v2; see CPython/LICENSE for details.
*/
#include "recordobj.h"
static PyObject * record_iter(PyObject *);
static PyObject * record_new_items_iter(PyObject *);
static ApgRecordObject *free_list[ApgRecord_MAXSAVESIZE];
static int numfree[ApgRecord_MAXSAVESIZE];
PyObject *
ApgRecord_New(PyObject *desc, Py_ssize_t size)
{
ApgRecordObject *o;
Py_ssize_t i;
if (size < 0 || desc == NULL || !ApgRecordDesc_CheckExact(desc)) {
PyErr_BadInternalCall();
return NULL;
}
if (size < ApgRecord_MAXSAVESIZE && (o = free_list[size]) != NULL) {
free_list[size] = (ApgRecordObject *) o->ob_item[0];
numfree[size]--;
_Py_NewReference((PyObject *)o);
}
else {
/* Check for overflow */
if ((size_t)size > ((size_t)PY_SSIZE_T_MAX - sizeof(ApgRecordObject) -
sizeof(PyObject *)) / sizeof(PyObject *)) {
return PyErr_NoMemory();
}
o = PyObject_GC_NewVar(ApgRecordObject, &ApgRecord_Type, size);
if (o == NULL) {
return NULL;
}
}
for (i = 0; i < size; i++) {
o->ob_item[i] = NULL;
}
Py_INCREF(desc);
o->desc = (ApgRecordDescObject*)desc;
o->self_hash = -1;
PyObject_GC_Track(o);
return (PyObject *) o;
}
static void
record_dealloc(ApgRecordObject *o)
{
Py_ssize_t i;
Py_ssize_t len = Py_SIZE(o);
PyObject_GC_UnTrack(o);
o->self_hash = -1;
Py_CLEAR(o->desc);
Py_TRASHCAN_SAFE_BEGIN(o)
if (len > 0) {
i = len;
while (--i >= 0) {
Py_CLEAR(o->ob_item[i]);
}
if (len < ApgRecord_MAXSAVESIZE &&
numfree[len] < ApgRecord_MAXFREELIST &&
ApgRecord_CheckExact(o))
{
o->ob_item[0] = (PyObject *) free_list[len];
numfree[len]++;
free_list[len] = o;
goto done; /* return */
}
}
Py_TYPE(o)->tp_free((PyObject *)o);
done:
Py_TRASHCAN_SAFE_END(o)
}
static int
record_traverse(ApgRecordObject *o, visitproc visit, void *arg)
{
Py_ssize_t i;
Py_VISIT(o->desc);
for (i = Py_SIZE(o); --i >= 0;) {
if (o->ob_item[i] != NULL) {
Py_VISIT(o->ob_item[i]);
}
}
return 0;
}
static Py_ssize_t
record_length(ApgRecordObject *o)
{
return Py_SIZE(o);
}
static Py_hash_t
record_hash(ApgRecordObject *v)
{
Py_uhash_t x; /* Unsigned for defined overflow behavior. */
Py_hash_t y;
Py_ssize_t len;
PyObject **p;
Py_uhash_t mult;
if (v->self_hash != -1) {
return v->self_hash;
}
len = Py_SIZE(v);
mult = _PyHASH_MULTIPLIER;
x = 0x345678UL;
p = v->ob_item;
while (--len >= 0) {
y = PyObject_Hash(*p++);
if (y == -1) {
return -1;
}
x = (x ^ (Py_uhash_t)y) * mult;
/* the cast might truncate len; that doesn't change hash stability */
mult += (Py_uhash_t)(82520UL + (size_t)len + (size_t)len);
}
x += 97531UL;
if (x == (Py_uhash_t)-1) {
x = (Py_uhash_t)-2;
}
v->self_hash = (Py_hash_t)x;
return (Py_hash_t)x;
}
static PyObject *
record_richcompare(PyObject *v, PyObject *w, int op)
{
Py_ssize_t i;
Py_ssize_t vlen, wlen;
int v_is_tuple = 0;
int w_is_tuple = 0;
int comp;
if (!ApgRecord_CheckExact(v)) {
if (!PyTuple_Check(v)) {
Py_RETURN_NOTIMPLEMENTED;
}
v_is_tuple = 1;
}
if (!ApgRecord_CheckExact(w)) {
if (!PyTuple_Check(w)) {
Py_RETURN_NOTIMPLEMENTED;
}
w_is_tuple = 1;
}
#define V_ITEM(i) \
(v_is_tuple ? (PyTuple_GET_ITEM(v, i)) : (ApgRecord_GET_ITEM(v, i)))
#define W_ITEM(i) \
(w_is_tuple ? (PyTuple_GET_ITEM(w, i)) : (ApgRecord_GET_ITEM(w, i)))
vlen = Py_SIZE(v);
wlen = Py_SIZE(w);
if (op == Py_EQ && vlen != wlen) {
/* Checking if v == w, but len(v) != len(w): return False */
Py_RETURN_FALSE;
}
if (op == Py_NE && vlen != wlen) {
/* Checking if v != w, and len(v) != len(w): return True */
Py_RETURN_TRUE;
}
/* Search for the first index where items are different.
* Note that because tuples are immutable, it's safe to reuse
* vlen and wlen across the comparison calls.
*/
for (i = 0; i < vlen && i < wlen; i++) {
comp = PyObject_RichCompareBool(V_ITEM(i), W_ITEM(i), Py_EQ);
if (comp < 0) {
return NULL;
}
if (!comp) {
break;
}
}
if (i >= vlen || i >= wlen) {
/* No more items to compare -- compare sizes */
int cmp;
switch (op) {
case Py_LT: cmp = vlen < wlen; break;
case Py_LE: cmp = vlen <= wlen; break;
case Py_EQ: cmp = vlen == wlen; break;
case Py_NE: cmp = vlen != wlen; break;
case Py_GT: cmp = vlen > wlen; break;
case Py_GE: cmp = vlen >= wlen; break;
default: return NULL; /* cannot happen */
}
if (cmp) {
Py_RETURN_TRUE;
}
else {
Py_RETURN_FALSE;
}
}
/* We have an item that differs -- shortcuts for EQ/NE */
if (op == Py_EQ) {
Py_RETURN_FALSE;
}
if (op == Py_NE) {
Py_RETURN_TRUE;
}
/* Compare the final item again using the proper operator */
return PyObject_RichCompare(V_ITEM(i), W_ITEM(i), op);
#undef V_ITEM
#undef W_ITEM
}
static PyObject *
record_item(ApgRecordObject *o, Py_ssize_t i)
{
if (i < 0 || i >= Py_SIZE(o)) {
PyErr_SetString(PyExc_IndexError, "record index out of range");
return NULL;
}
Py_INCREF(o->ob_item[i]);
return o->ob_item[i];
}
typedef enum item_by_name_result {
APG_ITEM_FOUND = 0,
APG_ERROR = -1,
APG_ITEM_NOT_FOUND = -2
} item_by_name_result_t;
/* Lookup a record value by its name. Return 0 on success, -2 if the
* value was not found (with KeyError set), and -1 on all other errors.
*/
static item_by_name_result_t
record_item_by_name(ApgRecordObject *o, PyObject *item, PyObject **result)
{
PyObject *mapped;
PyObject *val;
Py_ssize_t i;
mapped = PyObject_GetItem(o->desc->mapping, item);
if (mapped == NULL) {
goto noitem;
}
if (!PyIndex_Check(mapped)) {
Py_DECREF(mapped);
goto error;
}
i = PyNumber_AsSsize_t(mapped, PyExc_IndexError);
Py_DECREF(mapped);
if (i < 0) {
if (PyErr_Occurred())
PyErr_Clear();
goto error;
}
val = record_item(o, i);
if (val == NULL) {
PyErr_Clear();
goto error;
}
*result = val;
return APG_ITEM_FOUND;
noitem:
PyErr_SetObject(PyExc_KeyError, item);
return APG_ITEM_NOT_FOUND;
error:
PyErr_SetString(PyExc_RuntimeError, "invalid record descriptor");
return APG_ERROR;
}
static PyObject *
record_subscript(ApgRecordObject* o, PyObject* item)
{
if (PyIndex_Check(item)) {
Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
if (i == -1 && PyErr_Occurred())
return NULL;
if (i < 0) {
i += Py_SIZE(o);
}
return record_item(o, i);
}
else if (PySlice_Check(item)) {
Py_ssize_t start, stop, step, slicelength, cur, i;
PyObject* result;
PyObject* it;
PyObject **src, **dest;
if (PySlice_GetIndicesEx(
item,
Py_SIZE(o),
&start, &stop, &step, &slicelength) < 0)
{
return NULL;
}
if (slicelength <= 0) {
return PyTuple_New(0);
}
else {
result = PyTuple_New(slicelength);
if (!result) return NULL;
Loading ...