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)
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;
PyObject *gmapmeth_iteritems(PyObject *me)
{ return (gmap_mkiter(me, itemiter_pytype)); }
+#endif
+
PyObject *gmap_pyiter(PyObject *me)
{ return gmap_mkiter(me, keyiter_pytype); }
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 (;;) {
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)
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 -------------------------------------------------*/
/*----- Python version compatibility hacks --------------------------------*/
+/* Explicit version switching. */
+#if PY_VERSION_HEX >= 0x03000000
+# define PY3 1
+# define PY23(two, three) three
+#else
+# define PY2 1
+# define PY23(two, three) two
+#endif
+
/* The handy `Py_TYPE' and `Py_SIZE' macros turned up in 2.6. Define them if
* they're not already here.
*/
# define PyVarObject_HEAD_INIT(super, sz) PyObject_HEAD_INIT(super) sz,
#endif
+/* Python 3 doesn't have `int', only `long', even though it's called `int' at
+ * the Python level. Provide some obvious macros to fill in the gaps.
+ */
+#ifdef PY3
+# define PyInt_Check PyLong_Check
+# define PyInt_FromLong PyLong_FromLong
+# define PyInt_AS_LONG PyLong_AS_LONG
+# define PyInt_AsLong PyLong_AsLong
+# define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask
+# define PyNumber_Int PyNumber_Long
+#endif
+
/* Python 3.2 changed the type of hash values, so paper over this annoying
* difference.
*/
typedef long Py_hash_t;
#endif
+/* Python 3 always has the `CHECKTYPES' behaviour, and doesn't define the
+ * flag.
+ */
+#ifdef PY3
+# define Py_TPFLAGS_CHECKTYPES 0
+#endif
+
/* Plain octet strings. Python 2 calls these `str', while Python 3 calls
* them `bytes'. We call them `bin' here, and define the following.
*
# * `YN' is a format character for `PyArg_ParseTuple...' for retrieving an
* octet string and length from any sort-of vaguely binary-ish object.
*/
+#ifdef PY3
+# define BINOBJ PyBytesObject
+# define BIN_TYPE PyBytes_Type
+# define BIN_CHECK(obj) PyBytes_Check(obj)
+# define BIN_PTR(obj) PyBytes_AS_STRING(obj)
+# define BIN_LEN(obj) PyBytes_GET_SIZE(obj)
+# define BIN_FROMSTR(str) PyBytes_FromString(str)
+# define BIN_FROMSTRLEN(str, len) PyBytes_FromStringAndSize(str, len)
+# define BIN_FORMAT PyBytes_FromFormat
+# define BIN_VFORMAT PyBytes_FromFormatV
+# define BIN_PREPAREWRITE(obj, ptr, sz) do { \
+ (obj) = PyBytes_FromStringAndSize(0, (sz)); \
+ (ptr) = PyBytes_AS_STRING(obj); \
+ } while (0)
+# define BIN_DONEWRITE(obj, sz) do Py_SIZE(obj) = (sz); while (0)
+# define BIN_SETLEN(obj, len) do Py_SIZE(obj) = (len); while (0)
+# define Y "y"
+# define YN "y#"
+#else
# define BINOBJ PyStringObject
# define BIN_TYPE PyString_Type
# define BIN_CHECK(obj) PyString_Check(obj)
# define BIN_SETLEN(obj, len) do Py_SIZE(obj) = (len); while (0)
# define Y "s"
# define YN "s#"
+#endif
/* Text strings. Both Python 2 and Python 3 call these `str', but they're
* very different because a Python 3 `str' is Unicode inside. When dealing
*
* (Use `s' and `s#' in `PyArg_ParseTuple...'.)
*/
+#ifdef PY3
+# define TEXTOBJ PyUnicodeObject
+# define TEXT_TYPE PyUnicode_Type
+# define TEXT_CHECK(obj) PyUnicode_Check(obj)
+# if PY_VERSION_HEX >= 0x03030000
+# define TEXT_PTR(obj) PyUnicode_AsUTF8(obj)
+# define TEXT_STR(obj) PyUnicode_AsUTF8(obj)
+# define TEXT_PTRLEN(obj, ptr, len) do { \
+ Py_ssize_t len_; \
+ (ptr) = PyUnicode_AsUTF8AndSize((obj), &len_); \
+ (len) = len_; \
+ } while (0)
+# define TEXT_PREPAREWRITE(obj, ptr, sz) do { \
+ (obj) = PyUnicode_New((sz), 127); \
+ (ptr) = PyUnicode_DATA(obj); \
+ } while (0)
+# define TEXT_DONEWRITE(obj, len) do { \
+ size_t len_ = (len); \
+ assert(PyUnicode_IS_COMPACT_ASCII(obj)); \
+ ((char *)PyUnicode_DATA(obj))[len_] = 0; \
+ ((PyASCIIObject *)(obj))->length = len_; \
+ } while (0)
+# else
+# define TEXT_PTR(obj) _PyUnicode_AsString(obj)
+# define TEXT_STR(obj) _PyUnicode_AsString(obj)
+# define TEXT_PTRLEN(obj, ptr, len) do { \
+ Py_ssize_t len_; \
+ (ptr) = _PyUnicode_AsStringAndSize((obj), &len_); \
+ (len) = len_; \
+ } while (0)
+# define TEXT_PREPAREWRITE(obj, ptr, sz) do { \
+ (obj) = PyBytes_FromStringAndSize(0, (sz)); \
+ (ptr) = PyBytes_AS_STRING(obj); \
+ } while (0)
+# define TEXT_DONEWRITE(obj, len) do { \
+ PyObject *new_; \
+ Py_SIZE(obj) = (len); \
+ new_ = PyUnicode_FromEncodedObject(obj, 0, 0); \
+ assert(new_); Py_DECREF(obj); (obj) = new_; \
+ } while (0)
+# endif
+# define TEXT_FORMAT PyUnicode_FromFormat
+# define TEXT_VFORMAT PyUnicode_FromFormatV
+# define TEXT_FROMSTR(str) PyUnicode_FromString(str)
+# define TEXT_FROMSTRLEN(str, len) PyUnicode_FromStringAndSize(str, len)
+#else
# define TEXTOBJ PyStringObject
# define TEXT_TYPE PyString_Type
# define TEXT_CHECK(obj) PyString_Check(obj)
# define TEXT_DONEWRITE(obj, sz) do { Py_SIZE(obj) = (sz); } while (0)
# define TEXT_FROMSTR(str) PyString_FromString(str)
# define TEXT_FROMSTRLEN(str, len) PyString_FromStringAndSize(str, len)
+#endif
/*----- Utilities for returning values and exceptions ---------------------*/
#define GMAP_NAMETHDECL(func, doc) \
extern PyObject *gmapmeth_##func(PyObject *);
+#ifdef PY3
+# define GMAP_DOROMETHODS(METH, KWMETH, NAMETH) \
+ NAMETH(keys, "D.keys() -> LIST") \
+ NAMETH(values, "D.values() -> LIST") \
+ NAMETH(items, "D.items() -> LIST") \
+ KWMETH(get, "D.get(KEY, [default = None]) -> VALUE")
+#else
# define GMAP_DOROMETHODS(METH, KWMETH, NAMETH) \
METH (has_key, "D.has_key(KEY) -> BOOL") \
NAMETH(keys, "D.keys() -> LIST") \
NAMETH(itervalues, "D.itervalues() -> ITER") \
NAMETH(iteritems, "D.iteritems() -> ITER") \
KWMETH(get, "D.get(KEY, [default = None]) -> VALUE")
+#endif
#define GMAP_DOMETHODS(METH, KWMETH, NAMETH) \
GMAP_DOROMETHODS(METH, KWMETH, NAMETH) \