X-Git-Url: https://git.distorted.org.uk/~mdw/pyke/blobdiff_plain/83a72c0dad2f0da52131224e9b072ef3490db56f..c80de12d8d0827e0553fed2e4d392cb9bf3a378f:/mapping.c diff --git a/mapping.c b/mapping.c index 3ad1ac7..ad02d25 100644 --- a/mapping.c +++ b/mapping.c @@ -257,6 +257,354 @@ static const PyTypeObject itemiter_pytype_skel = { 0 /* @tp_is_gc@ */ }; +/*----- Mapping views -----------------------------------------------------*/ + +#ifdef PY3 + +static PyTypeObject *keyview_pytype, *itemview_pytype, *valview_pytype; + +typedef struct view_pyobj { + PyObject_HEAD + PyObject *map; +} view_pyobj; +#define VIEW_MAP(o) (((view_pyobj *)(o))->map) + +static PyObject *gmap_mkview(PyObject *me, PyTypeObject *ty) +{ + view_pyobj *v = PyObject_NEW(view_pyobj, ty); + v->map = me; Py_INCREF(me); + return ((PyObject *)v); +} + +static void view_pydealloc(PyObject *me) + { Py_DECREF(VIEW_MAP(me)); FREEOBJ(me); } + +#define BINOP(op, update) \ + static PyObject *view_py##op(PyObject *me, PyObject *you) \ + { \ + PyObject *set = 0; \ + PyObject *rc = 0; \ + \ + set = PySet_New(me); if (!set) goto end; \ + if (!PyObject_CallMethod(set, #update, "(O)", you)) goto end; \ + rc = set; set = 0; \ + end: \ + Py_XDECREF(set); \ + return (rc); \ + } +BINOP(and, intersection_update) +BINOP(or, update) +BINOP(xor, symmetric_difference_update) +#undef BINOP + +static int all_contained_p(PyObject *x, PyObject *y) +{ + PyObject *i = 0, *e; + int b, rc = -1; + + i = PyObject_GetIter(x); if (!i) goto end; + for (;;) { + e = PyIter_Next(i); if (!e) break; + b = PySequence_Contains(y, e); Py_DECREF(e); if (b < 0) goto end; + if (!b) { rc = 0; goto end; } + } + if (PyErr_Occurred()) goto end; + rc = 1; +end: + Py_XDECREF(i); + return (rc); +} + +static Py_ssize_t view_pysize(PyObject *me) + { return (PyMapping_Size(VIEW_MAP(me))); } + +static PyObject *view_pyrichcompare(PyObject *me, PyObject *you, int op) +{ + PyObject *map = ITER_MAP(me); + Py_ssize_t mysz, yoursz; + int b; + + mysz = PyMapping_Size(map); if (mysz < 0) return (0); + yoursz = PyObject_Size(you); + if (yoursz < 0) { PyErr_Clear(); RETURN_NOTIMPL; } + + switch (op) { + case Py_EQ: + if (mysz != yoursz) RETURN_FALSE; + b = all_contained_p(you, me); + break; + case Py_NE: + if (mysz != yoursz) RETURN_TRUE; + b = all_contained_p(you, me); + break; + case Py_LT: + if (mysz >= yoursz) RETURN_FALSE; + b = all_contained_p(me, you); + break; + case Py_LE: + if (mysz > yoursz) RETURN_FALSE; + b = all_contained_p(me, you); + break; + case Py_GE: + if (mysz < yoursz) RETURN_FALSE; + b = all_contained_p(you, me); + break; + case Py_GT: + if (mysz <= yoursz) RETURN_FALSE; + b = all_contained_p(you, me); + break; + default: + abort(); + } + if (b < 0) return (0); + return (getbool(b)); +} + +static PyObject *keyview_pyiter(PyObject *me) + { return (gmap_mkiter(VIEW_MAP(me), keyiter_pytype)); } + +static int keyview_pyhaskey(PyObject *me, PyObject *k) +{ + PyObject *map = VIEW_MAP(me); + const struct gmap_ops *gmops = GMAP_OPS(map); + return (gmops->lookup(map, k, 0) ? 1 : PyErr_Occurred() ? -1 : 0); +} + +static int itemview_pyhaskey(PyObject *me, PyObject *it) +{ + PyObject *map = VIEW_MAP(me); + const struct gmap_ops *gmops = GMAP_OPS(map); + void *e; + int b; + PyObject *v; + + if (!PyTuple_Check(it) || PyTuple_GET_SIZE(it) != 2) return (0); + e = gmops->lookup(map, PyTuple_GET_ITEM(it, 0), 0); + if (!e) return (PyErr_Occurred() ? -1 : 0); + v = gmops->entry_value(map, e); if (!v) return (-1); + b = PyObject_RichCompareBool(v, PyTuple_GET_ITEM(it, 1), Py_EQ); + Py_DECREF(v); return (b); +} + +static PyObject *valview_pyiter(PyObject *me) + { return (gmap_mkiter(VIEW_MAP(me), valiter_pytype)); } + +static PyObject *itemview_pyiter(PyObject *me) + { return (gmap_mkiter(VIEW_MAP(me), itemiter_pytype)); } + +static const PyNumberMethods view_pynumber = { + 0, /* @nb_add@ */ + 0, /* @nb_subtract@ */ + 0, /* @nb_multiply@ */ +#ifdef PY2 + 0, /* @nb_divide@ */ +#endif + 0, /* @nb_remainder@ */ + 0, /* @nb_divmod@ */ + 0, /* @nb_power@ */ + 0, /* @nb_negative@ */ + 0, /* @nb_positive@ */ + 0, /* @nb_absolute@ */ + 0, /* @nb_nonzero@ */ + 0, /* @nb_invert@ */ + 0, /* @nb_lshift@ */ + 0, /* @nb_rshift@ */ + view_pyand, /* @nb_and@ */ + view_pyxor, /* @nb_xor@ */ + view_pyor, /* @nb_or@ */ + 0, /* @nb_coerce@ */ + 0, /* @nb_int@ */ + 0, /* @nb_long@ */ + 0, /* @nb_float@ */ + 0, /* @nb_oct@ */ + 0, /* @nb_hex@ */ +}; + +static const PySequenceMethods keyview_pysequence = { + view_pysize, /* @sq_length@ */ + 0, /* @sq_concat@ */ + 0, /* @sq_repeat@ */ + 0, /* @sq_item@ */ + 0, /* @sq_slice@ */ + 0, /* @sq_ass_item@ */ + 0, /* @sq_ass_slice@ */ + keyview_pyhaskey, /* @sq_contains@ */ + 0, /* @sq_inplace_concat@ */ + 0, /* @sq_inplace_repeat@ */ +}; + +static const PyTypeObject keyview_pytype_skel = { + PyVarObject_HEAD_INIT(0, 0) /* Header */ + "_KeyView", /* @tp_name@ */ + sizeof(view_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + view_pydealloc, /* @tp_dealloc@ */ + 0, /* @tp_print@ */ + 0, /* @tp_getattr@ */ + 0, /* @tp_setattr@ */ + 0, /* @tp_compare@ */ + 0, /* @tp_repr@ */ + PYNUMBER(view), /* @tp_as_number@ */ + PYSEQUENCE(keyview), /* @tp_as_sequence@ */ + 0, /* @tp_as_mapping@ */ + 0, /* @tp_hash@ */ + 0, /* @tp_call@ */ + 0, /* @tp_str@ */ + 0, /* @tp_getattro@ */ + 0, /* @tp_setattro@ */ + 0, /* @tp_as_buffer@ */ + Py_TPFLAGS_DEFAULT | /* @tp_flags@ */ + Py_TPFLAGS_BASETYPE, + + /* @tp_doc@ */ + "View of a mapping's keys.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + view_pyrichcompare, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + keyview_pyiter, /* @tp_iter@ */ + 0, /* @tp_iternext@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + 0, /* @tp_getset@ */ + 0, /* @tp_base@ */ + 0, /* @tp_dict@ */ + 0, /* @tp_descr_get@ */ + 0, /* @tp_descr_set@ */ + 0, /* @tp_dictoffset@ */ + 0, /* @tp_init@ */ + PyType_GenericAlloc, /* @tp_alloc@ */ + abstract_pynew, /* @tp_new@ */ + 0, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static const PySequenceMethods valview_pysequence = { + view_pysize, /* @sq_length@ */ + 0, /* @sq_concat@ */ + 0, /* @sq_repeat@ */ + 0, /* @sq_item@ */ + 0, /* @sq_slice@ */ + 0, /* @sq_ass_item@ */ + 0, /* @sq_ass_slice@ */ + 0, /* @sq_contains@ */ + 0, /* @sq_inplace_concat@ */ + 0, /* @sq_inplace_repeat@ */ +}; + +static const PyTypeObject valview_pytype_skel = { + PyVarObject_HEAD_INIT(0, 0) /* Header */ + "_ValueView", /* @tp_name@ */ + sizeof(view_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + view_pydealloc, /* @tp_dealloc@ */ + 0, /* @tp_print@ */ + 0, /* @tp_getattr@ */ + 0, /* @tp_setattr@ */ + 0, /* @tp_compare@ */ + 0, /* @tp_repr@ */ + PYNUMBER(view), /* @tp_as_number@ */ + PYSEQUENCE(valview), /* @tp_as_sequence@ */ + 0, /* @tp_as_mapping@ */ + 0, /* @tp_hash@ */ + 0, /* @tp_call@ */ + 0, /* @tp_str@ */ + 0, /* @tp_getattro@ */ + 0, /* @tp_setattro@ */ + 0, /* @tp_as_buffer@ */ + Py_TPFLAGS_DEFAULT | /* @tp_flags@ */ + Py_TPFLAGS_BASETYPE, + + /* @tp_doc@ */ + "View of a mapping's values.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + valview_pyiter, /* @tp_iter@ */ + 0, /* @tp_iternext@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + 0, /* @tp_getset@ */ + 0, /* @tp_base@ */ + 0, /* @tp_dict@ */ + 0, /* @tp_descr_get@ */ + 0, /* @tp_descr_set@ */ + 0, /* @tp_dictoffset@ */ + 0, /* @tp_init@ */ + PyType_GenericAlloc, /* @tp_alloc@ */ + abstract_pynew, /* @tp_new@ */ + 0, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static const PySequenceMethods itemview_pysequence = { + view_pysize, /* @sq_length@ */ + 0, /* @sq_concat@ */ + 0, /* @sq_repeat@ */ + 0, /* @sq_item@ */ + 0, /* @sq_slice@ */ + 0, /* @sq_ass_item@ */ + 0, /* @sq_ass_slice@ */ + itemview_pyhaskey, /* @sq_contains@ */ + 0, /* @sq_inplace_concat@ */ + 0, /* @sq_inplace_repeat@ */ +}; + +static const PyTypeObject itemview_pytype_skel = { + PyVarObject_HEAD_INIT(0, 0) /* Header */ + "_ItemView", /* @tp_name@ */ + sizeof(view_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + view_pydealloc, /* @tp_dealloc@ */ + 0, /* @tp_print@ */ + 0, /* @tp_getattr@ */ + 0, /* @tp_setattr@ */ + 0, /* @tp_compare@ */ + 0, /* @tp_repr@ */ + PYNUMBER(view), /* @tp_as_number@ */ + PYSEQUENCE(itemview), /* @tp_as_sequence@ */ + 0, /* @tp_as_mapping@ */ + 0, /* @tp_hash@ */ + 0, /* @tp_call@ */ + 0, /* @tp_str@ */ + 0, /* @tp_getattro@ */ + 0, /* @tp_setattro@ */ + 0, /* @tp_as_buffer@ */ + Py_TPFLAGS_DEFAULT | /* @tp_flags@ */ + Py_TPFLAGS_BASETYPE, + + /* @tp_doc@ */ + "View of a mapping's key/value items.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + view_pyrichcompare, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + itemview_pyiter, /* @tp_iter@ */ + 0, /* @tp_iternext@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + 0, /* @tp_getset@ */ + 0, /* @tp_base@ */ + 0, /* @tp_dict@ */ + 0, /* @tp_descr_get@ */ + 0, /* @tp_descr_set@ */ + 0, /* @tp_dictoffset@ */ + 0, /* @tp_init@ */ + PyType_GenericAlloc, /* @tp_alloc@ */ + abstract_pynew, /* @tp_new@ */ + 0, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +#endif + /*----- Other mapping protocol support ------------------------------------*/ Py_ssize_t gmap_pysize(PyObject *me) @@ -319,6 +667,19 @@ const PySequenceMethods gmap_pysequence = { 0 /* @sq_inplace_repeat@ */ }; +#ifdef PY3 + +PyObject *gmapmeth_keys(PyObject *me) + { return (gmap_mkview(me, keyview_pytype)); } + +PyObject *gmapmeth_values(PyObject *me) + { return (gmap_mkview(me, valview_pytype)); } + +PyObject *gmapmeth_items(PyObject *me) + { return (gmap_mkview(me, itemview_pytype)); } + +#else + PyObject *gmapmeth_has_key(PyObject *me, PyObject *arg) { PyObject *k; @@ -408,6 +769,8 @@ PyObject *gmapmeth_itervalues(PyObject *me) PyObject *gmapmeth_iteritems(PyObject *me) { return (gmap_mkiter(me, itemiter_pytype)); } +#endif + PyObject *gmap_pyiter(PyObject *me) { return gmap_mkiter(me, keyiter_pytype); } @@ -493,8 +856,12 @@ static int update_core(PyObject *me, PyObject *map) unsigned foundp; int rc = -1; - v = PyObject_CallMethod(map, "iteritems", 0); + v = PyObject_CallMethod(map, PY23("iteritems", "items"), 0); +#ifdef PY3 + if (v) { i = PyObject_GetIter(v); Py_DECREF(v); v = 0; } +#else i = v; v = 0; +#endif if (i) { for (;;) { @@ -576,6 +943,11 @@ void pyke_gmap_pyinit(void) INITTYPE(keyiter, root); INITTYPE(itemiter, root); INITTYPE(valiter, root); +#ifdef PY3 + INITTYPE(keyview, root); + INITTYPE(valview, root); + INITTYPE(itemview, root); +#endif } void pyke_gmap_pyinsert(PyObject *mod) @@ -583,6 +955,11 @@ void pyke_gmap_pyinsert(PyObject *mod) INSERT("_KeyIter", keyiter_pytype); INSERT("_ValueIter", valiter_pytype); INSERT("_ItemIter", itemiter_pytype); +#ifdef PY3 + INSERT("_KeyView", keyview_pytype); + INSERT("_ValueView", valview_pytype); + INSERT("_ItemView", itemview_pytype); +#endif } /*----- That's all, folks -------------------------------------------------*/