3 * Generic mapping support
5 * (c) 2019 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Pyke: the Python Kit for Extensions.
12 * Pyke is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU General Public License as published by the Free
14 * Software Foundation; either version 2 of the License, or (at your
15 * option) any later version.
17 * Pyke is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * You should have received a copy of the GNU General Public License
23 * along with Pyke. If not, write to the Free Software Foundation, Inc.,
24 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 /*----- Header files ------------------------------------------------------*/
31 /*----- Iteration ---------------------------------------------------------*/
33 static PyTypeObject
*keyiter_pytype
, *itemiter_pytype
, *valiter_pytype
;
40 typedef struct iter_pyobj
{
45 #define ITER_MAP(o) (((iter_pyobj *)(o))->map)
46 #define ITER_ITER(o) (&((iter_pyobj *)(o))->iter)
47 #define ITER_EXTERNALP(o) \
48 (GMAP_OPS(ITER_MAP(o))->isz > sizeof(union iterstate))
49 #define ITER_I(o) (ITER_EXTERNALP(o) ? ITER_ITER(o)->external \
50 : &ITER_ITER(o)->internal)
52 static void *iter_init(PyObject
*me
, union iterstate
*iter
)
54 const gmap_ops
*gmops
= GMAP_OPS(me
);
57 if (gmops
->isz
<= sizeof(*iter
)) i
= &iter
->internal
;
58 else { i
= iter
->external
= PyObject_Malloc(gmops
->isz
); assert(i
); }
59 gmops
->iter_init(me
, i
);
63 static void iter_free(PyObject
*me
, union iterstate
*iter
)
64 { if (GMAP_OPS(me
)->isz
> sizeof(*iter
)) PyObject_Free(iter
->external
); }
66 static void iter_pydealloc(PyObject
*me
)
68 PyObject
*map
= ITER_MAP(me
);
69 iter_free(map
, ITER_ITER(me
));
70 Py_DECREF(map
); FREEOBJ(me
);
73 static PyObject
*gmap_mkiter(PyObject
*me
, PyTypeObject
*ty
)
75 iter_pyobj
*iter
= PyObject_NEW(iter_pyobj
, ty
);
77 iter
->map
= me
; Py_INCREF(me
);
78 iter_init(me
, &iter
->iter
);
79 return ((PyObject
*)iter
);
82 static PyObject
*keyiter_pynext(PyObject
*me
)
84 PyObject
*map
= ITER_MAP(me
);
85 const struct gmap_ops
*gmops
= GMAP_OPS(map
);
86 void *e
= gmops
->iter_next(map
, ITER_I(me
));
89 else return (gmops
->entry_key(map
, e
));
92 static const PyTypeObject keyiter_pytype_skel
= {
93 PyVarObject_HEAD_INIT(0, 0) /* Header */
94 "_KeyIter", /* @tp_name@ */
95 sizeof(iter_pyobj
), /* @tp_basicsize@ */
96 0, /* @tp_itemsize@ */
98 iter_pydealloc
, /* @tp_dealloc@ */
100 0, /* @tp_getattr@ */
101 0, /* @tp_setattr@ */
102 0, /* @tp_compare@ */
104 0, /* @tp_as_number@ */
105 0, /* @tp_as_sequence@ */
106 0, /* @tp_as_mapping@ */
110 0, /* @tp_getattro@ */
111 0, /* @tp_setattro@ */
112 0, /* @tp_as_buffer@ */
113 Py_TPFLAGS_DEFAULT
| /* @tp_flags@ */
117 "Iterates over the keys of a mapping.",
119 0, /* @tp_traverse@ */
121 0, /* @tp_richcompare@ */
122 0, /* @tp_weaklistoffset@ */
123 PyObject_SelfIter
, /* @tp_iter@ */
124 keyiter_pynext
, /* @tp_iternext@ */
125 0, /* @tp_methods@ */
126 0, /* @tp_members@ */
130 0, /* @tp_descr_get@ */
131 0, /* @tp_descr_set@ */
132 0, /* @tp_dictoffset@ */
134 PyType_GenericAlloc
, /* @tp_alloc@ */
135 abstract_pynew
, /* @tp_new@ */
140 static PyObject
*valiter_pynext(PyObject
*me
)
142 PyObject
*map
= ITER_MAP(me
);
143 const struct gmap_ops
*gmops
= GMAP_OPS(map
);
144 void *e
= gmops
->iter_next(map
, ITER_I(me
));
147 else return (gmops
->entry_value(map
, e
));
150 static const PyTypeObject valiter_pytype_skel
= {
151 PyVarObject_HEAD_INIT(0, 0) /* Header */
152 "_ValueIter", /* @tp_name@ */
153 sizeof(iter_pyobj
), /* @tp_basicsize@ */
154 0, /* @tp_itemsize@ */
156 iter_pydealloc
, /* @tp_dealloc@ */
158 0, /* @tp_getattr@ */
159 0, /* @tp_setattr@ */
160 0, /* @tp_compare@ */
162 0, /* @tp_as_number@ */
163 0, /* @tp_as_sequence@ */
164 0, /* @tp_as_mapping@ */
168 0, /* @tp_getattro@ */
169 0, /* @tp_setattro@ */
170 0, /* @tp_as_buffer@ */
171 Py_TPFLAGS_DEFAULT
| /* @tp_flags@ */
175 "Iterates over the values of a mapping.",
177 0, /* @tp_traverse@ */
179 0, /* @tp_richcompare@ */
180 0, /* @tp_weaklistoffset@ */
181 PyObject_SelfIter
, /* @tp_iter@ */
182 valiter_pynext
, /* @tp_iternext@ */
183 0, /* @tp_methods@ */
184 0, /* @tp_members@ */
188 0, /* @tp_descr_get@ */
189 0, /* @tp_descr_set@ */
190 0, /* @tp_dictoffset@ */
192 PyType_GenericAlloc
, /* @tp_alloc@ */
193 abstract_pynew
, /* @tp_new@ */
198 static PyObject
*itemiter_pynext(PyObject
*me
)
200 PyObject
*map
= ITER_MAP(me
);
201 const struct gmap_ops
*gmops
= GMAP_OPS(map
);
202 void *e
= gmops
->iter_next(map
, ITER_I(me
));
206 rc
= Py_BuildValue("(NN)",
207 gmops
->entry_key(map
, e
),
208 gmops
->entry_value(map
, e
));
212 static const PyTypeObject itemiter_pytype_skel
= {
213 PyVarObject_HEAD_INIT(0, 0) /* Header */
214 "_ItemIter", /* @tp_name@ */
215 sizeof(iter_pyobj
), /* @tp_basicsize@ */
216 0, /* @tp_itemsize@ */
218 iter_pydealloc
, /* @tp_dealloc@ */
220 0, /* @tp_getattr@ */
221 0, /* @tp_setattr@ */
222 0, /* @tp_compare@ */
224 0, /* @tp_as_number@ */
225 0, /* @tp_as_sequence@ */
226 0, /* @tp_as_mapping@ */
230 0, /* @tp_getattro@ */
231 0, /* @tp_setattro@ */
232 0, /* @tp_as_buffer@ */
233 Py_TPFLAGS_DEFAULT
| /* @tp_flags@ */
237 "Iterates over the items of a mapping.",
239 0, /* @tp_traverse@ */
241 0, /* @tp_richcompare@ */
242 0, /* @tp_weaklistoffset@ */
243 PyObject_SelfIter
, /* @tp_iter@ */
244 itemiter_pynext
, /* @tp_iternext@ */
245 0, /* @tp_methods@ */
246 0, /* @tp_members@ */
250 0, /* @tp_descr_get@ */
251 0, /* @tp_descr_set@ */
252 0, /* @tp_dictoffset@ */
254 PyType_GenericAlloc
, /* @tp_alloc@ */
255 abstract_pynew
, /* @tp_new@ */
260 /*----- Other mapping protocol support ------------------------------------*/
262 Py_ssize_t
gmap_pysize(PyObject
*me
)
264 const gmap_ops
*gmops
= GMAP_OPS(me
);
265 union iterstate iter
;
269 i
= iter_init(me
, &iter
);
270 while (gmops
->iter_next(me
, i
)) n
++;
271 iter_free(me
, &iter
);
275 PyObject
*gmap_pylookup(PyObject
*me
, PyObject
*key
)
277 const gmap_ops
*gmops
= GMAP_OPS(me
);
278 void *e
= gmops
->lookup(me
, key
, 0);
281 if (!e
) { if (!PyErr_Occurred()) MAPERR(key
); else goto end
; }
282 rc
= gmops
->entry_value(me
, e
);
287 int gmap_pystore(PyObject
*me
, PyObject
*key
, PyObject
*value
)
289 const gmap_ops
*gmops
= GMAP_OPS(me
);
291 void *e
= gmops
->lookup(me
, key
, &f
);
296 rc
= gmops
->del_entry(me
, e
);
298 rc
= gmops
->set_entry(me
, e
, value
);
299 if (rc
&& !f
) gmops
->del_entry(me
, e
);
306 int gmap_pyhaskey(PyObject
*me
, PyObject
*key
)
307 { return (GMAP_OPS(me
)->lookup(me
, key
, 0) ?
1 : PyErr_Occurred() ?
-1 : 0); }
309 const PySequenceMethods gmap_pysequence
= {
315 0, /* @sq_ass_item@ */
316 0, /* @sq_ass_slice@ */
317 gmap_pyhaskey
, /* @sq_contains@ */
318 0, /* @sq_inplace_concat@ */
319 0 /* @sq_inplace_repeat@ */
322 PyObject
*gmapmeth_has_key(PyObject
*me
, PyObject
*arg
)
326 if (!PyArg_ParseTuple(arg
, "O:has_key", &k
)) return (0);
327 e
= GMAP_OPS(me
)->lookup(me
, k
, 0);
329 else if (!PyErr_Occurred()) RETURN_FALSE
;
333 PyObject
*gmapmeth_keys(PyObject
*me
)
335 const gmap_ops
*gmops
= GMAP_OPS(me
);
336 union iterstate iter
; void *i
= 0, *e
;
337 PyObject
*l
= 0, *k
, *rc
= 0;
340 if ((l
= PyList_New(0)) == 0) goto done
;
341 i
= iter_init(me
, &iter
);
342 while ((e
= gmops
->iter_next(me
, i
)) != 0) {
343 k
= gmops
->entry_key(me
, e
);
344 err
= PyList_Append(l
, k
);
351 if (i
) iter_free(me
, &iter
);
355 PyObject
*gmapmeth_values(PyObject
*me
)
357 const gmap_ops
*gmops
= GMAP_OPS(me
);
358 union iterstate iter
; void *i
= 0, *e
;
359 PyObject
*l
= 0, *v
, *rc
= 0;
362 if ((l
= PyList_New(0)) == 0) goto done
;
363 i
= iter_init(me
, &iter
);
364 while ((e
= gmops
->iter_next(me
, i
)) != 0) {
365 v
= gmops
->entry_value(me
, e
);
366 err
= PyList_Append(l
, v
);
373 if (i
) iter_free(me
, &iter
);
377 PyObject
*gmapmeth_items(PyObject
*me
)
379 const gmap_ops
*gmops
= GMAP_OPS(me
);
380 union iterstate iter
; void *i
= 0, *e
;
381 PyObject
*l
= 0, *z
, *rc
= 0;
384 if ((l
= PyList_New(0)) == 0) goto done
;
385 i
= iter_init(me
, &iter
);
386 while ((e
= gmops
->iter_next(me
, i
)) != 0) {
387 if ((z
= Py_BuildValue("(NN)",
388 gmops
->entry_key(me
, e
),
389 gmops
->entry_value(me
, e
))) == 0)
391 err
= PyList_Append(l
, z
);
398 if (i
) iter_free(me
, &iter
);
402 PyObject
*gmapmeth_iterkeys(PyObject
*me
)
403 { return (gmap_mkiter(me
, keyiter_pytype
)); }
405 PyObject
*gmapmeth_itervalues(PyObject
*me
)
406 { return (gmap_mkiter(me
, valiter_pytype
)); }
408 PyObject
*gmapmeth_iteritems(PyObject
*me
)
409 { return (gmap_mkiter(me
, itemiter_pytype
)); }
411 PyObject
*gmap_pyiter(PyObject
*me
)
412 { return gmap_mkiter(me
, keyiter_pytype
); }
414 PyObject
*gmapmeth_clear(PyObject
*me
)
416 const gmap_ops
*gmops
= GMAP_OPS(me
);
417 union iterstate iter
;
421 i
= iter_init(me
, &iter
);
423 e
= gmops
->iter_next(me
, i
); if (!e
) break;
424 if (gmops
->del_entry(me
, e
)) goto end
;
426 iter_free(me
, &iter
);
427 rc
= me
; Py_INCREF(me
);
432 static const char *const def_kwlist
[] = { "key", "default", 0 };
433 #define DEF_KWLIST ((/*unconst*/ char **)def_kwlist)
435 PyObject
*gmapmeth_get(PyObject
*me
, PyObject
*arg
, PyObject
*kw
)
437 const gmap_ops
*gmops
= GMAP_OPS(me
);
438 PyObject
*k
, *def
= Py_None
;
441 if (!PyArg_ParseTupleAndKeywords(arg
, kw
, "O|O:get", DEF_KWLIST
, &k
, &def
))
443 e
= gmops
->lookup(me
, k
, 0);
444 if (e
) return (gmops
->entry_value(me
, e
));
445 else if (!PyErr_Occurred()) RETURN_OBJ(def
);
449 PyObject
*gmapmeth_setdefault(PyObject
*me
, PyObject
*arg
, PyObject
*kw
)
451 const gmap_ops
*gmops
= GMAP_OPS(me
);
452 PyObject
*k
, *def
= Py_None
;
456 if (!PyArg_ParseTupleAndKeywords(arg
, kw
, "O|O:setdefault", DEF_KWLIST
,
459 e
= gmops
->lookup(me
, k
, &f
);
461 else if (f
) return (gmops
->entry_value(me
, e
));
462 else if (gmops
->set_entry(me
, e
, def
)) return (0);
463 else RETURN_OBJ(def
);
466 PyObject
*gmapmeth_pop(PyObject
*me
, PyObject
*arg
, PyObject
*kw
)
468 const gmap_ops
*gmops
= GMAP_OPS(me
);
469 PyObject
*k
, *def
= 0;
473 if (!PyArg_ParseTupleAndKeywords(arg
, kw
, "O|O:pop", DEF_KWLIST
, &k
, &def
))
475 e
= gmops
->lookup(me
, k
, 0);
477 if (PyErr_Occurred()) goto end
;
478 else if (def
) { rc
= def
; Py_INCREF(rc
); }
481 rc
= gmops
->entry_value(me
, e
);
482 if (gmops
->del_entry(me
, e
)) { Py_DECREF(rc
); rc
= 0; }
488 static int update_core(PyObject
*me
, PyObject
*map
)
490 const gmap_ops
*gmops
= GMAP_OPS(me
);
491 PyObject
*i
= 0, *item
= 0, *k
= 0, *v
= 0;
496 i
= PyObject_CallMethod(map
, "iteritems", 0);
500 item
= PyIter_Next(i
); if (!item
) break;
501 if (!PyTuple_Check(item
) || PyTuple_GET_SIZE(item
) != 2)
502 TYERR("wanted a pair");
503 k
= PyTuple_GET_ITEM(item
, 0); Py_INCREF(k
);
504 v
= PyTuple_GET_ITEM(item
, 1); Py_INCREF(v
);
505 e
= gmops
->lookup(me
, k
, &foundp
); if (!e
) goto end
;
506 if (gmops
->set_entry(me
, e
, v
)) goto end
;
507 Py_DECREF(item
); Py_DECREF(k
); Py_DECREF(v
); item
= k
= v
= 0;
509 if (PyErr_Occurred()) goto end
;
512 i
= PyObject_GetIter(map
); if (!i
) goto end
;
514 k
= PyIter_Next(i
); if (!k
) goto end
;
515 v
= PyObject_GetItem(map
, k
); if (!v
) goto end
;
516 e
= gmops
->lookup(me
, k
, &foundp
); if (!e
) goto end
;
517 if (gmops
->set_entry(me
, e
, v
)) goto end
;
518 Py_DECREF(k
); Py_DECREF(v
); k
= v
= 0;
520 if (PyErr_Occurred()) goto end
;
524 Py_XDECREF(i
); Py_XDECREF(item
);
525 Py_XDECREF(k
); Py_XDECREF(v
);
529 PyObject
*gmapmeth_update(PyObject
*me
, PyObject
*arg
, PyObject
*kw
)
533 if (!PyArg_ParseTuple(arg
, "|O:update", &map
)) return (0);
534 if (map
&& update_core(me
, map
)) return (0);
535 if (kw
&& update_core(me
, kw
)) return (0);
539 PyObject
*gmapmeth_popitem(PyObject
*me
)
541 const gmap_ops
*gmops
= GMAP_OPS(me
);
542 union iterstate iter
;
547 i
= iter_init(me
, &iter
);
548 e
= gmops
->iter_next(me
, i
);
549 iter_free(me
, &iter
);
553 rc
= Py_BuildValue("(NN)",
554 gmops
->entry_key(me
, e
), gmops
->entry_value(me
, e
));
555 if (gmops
->del_entry(me
, e
)) { Py_DECREF(rc
); rc
= 0; }
561 const PyMethodDef gmapro_pymethods
[] = {
566 const PyMethodDef gmap_pymethods
[] = {
571 /*----- Submodule initialization ------------------------------------------*/
573 void pyke_gmap_pyinit(void)
575 INITTYPE(keyiter
, root
);
576 INITTYPE(itemiter
, root
);
577 INITTYPE(valiter
, root
);
580 void pyke_gmap_pyinsert(PyObject
*mod
)
582 INSERT("_KeyIter", keyiter_pytype
);
583 INSERT("_ValueIter", valiter_pytype
);
584 INSERT("_ItemIter", itemiter_pytype
);
587 /*----- That's all, folks -------------------------------------------------*/