Commit | Line | Data |
---|---|---|
10e6f88a MW |
1 | /* -*-c-*- |
2 | * | |
3 | * Generic mapping support | |
4 | * | |
5 | * (c) 2019 Straylight/Edgeware | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of Pyke: the Python Kit for Extensions. | |
11 | * | |
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. | |
16 | * | |
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 | |
20 | * for more details. | |
21 | * | |
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. | |
25 | */ | |
26 | ||
27 | /*----- Header files ------------------------------------------------------*/ | |
28 | ||
29 | #include "pyke.h" | |
30 | ||
31 | /*----- Iteration ---------------------------------------------------------*/ | |
32 | ||
31ff254e MW |
33 | static PyTypeObject *keyiter_pytype, *itemiter_pytype, *valiter_pytype; |
34 | ||
35 | union iterstate { | |
36 | void *external; | |
37 | void *internal[4]; | |
38 | }; | |
10e6f88a MW |
39 | |
40 | typedef struct iter_pyobj { | |
41 | PyObject_HEAD | |
42 | PyObject *map; | |
31ff254e | 43 | union iterstate iter; |
10e6f88a MW |
44 | } iter_pyobj; |
45 | #define ITER_MAP(o) (((iter_pyobj *)(o))->map) | |
31ff254e MW |
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) | |
51 | ||
52 | static void *iter_init(PyObject *me, union iterstate *iter) | |
53 | { | |
54 | const gmap_ops *gmops = GMAP_OPS(me); | |
55 | void *i; | |
56 | ||
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); | |
60 | return (i); | |
61 | } | |
62 | ||
63 | static void iter_free(PyObject *me, union iterstate *iter) | |
64 | { if (GMAP_OPS(me)->isz > sizeof(*iter)) PyObject_Free(iter->external); } | |
10e6f88a MW |
65 | |
66 | static void iter_pydealloc(PyObject *me) | |
31ff254e MW |
67 | { |
68 | PyObject *map = ITER_MAP(me); | |
69 | iter_free(map, ITER_ITER(me)); | |
70 | Py_DECREF(map); FREEOBJ(me); | |
71 | } | |
10e6f88a | 72 | |
31ff254e | 73 | static PyObject *gmap_mkiter(PyObject *me, PyTypeObject *ty) |
10e6f88a | 74 | { |
31ff254e | 75 | iter_pyobj *iter = PyObject_NEW(iter_pyobj, ty); |
10e6f88a | 76 | |
31ff254e MW |
77 | iter->map = me; Py_INCREF(me); |
78 | iter_init(me, &iter->iter); | |
79 | return ((PyObject *)iter); | |
10e6f88a MW |
80 | } |
81 | ||
31ff254e MW |
82 | static PyObject *keyiter_pynext(PyObject *me) |
83 | { | |
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)); | |
87 | ||
88 | if (!e) return (0); | |
89 | else return (gmops->entry_key(map, e)); | |
90 | } | |
91 | ||
92 | static const PyTypeObject keyiter_pytype_skel = { | |
591bf41b | 93 | PyVarObject_HEAD_INIT(0, 0) /* Header */ |
31ff254e | 94 | "_KeyIter", /* @tp_name@ */ |
10e6f88a MW |
95 | sizeof(iter_pyobj), /* @tp_basicsize@ */ |
96 | 0, /* @tp_itemsize@ */ | |
97 | ||
98 | iter_pydealloc, /* @tp_dealloc@ */ | |
99 | 0, /* @tp_print@ */ | |
100 | 0, /* @tp_getattr@ */ | |
101 | 0, /* @tp_setattr@ */ | |
102 | 0, /* @tp_compare@ */ | |
103 | 0, /* @tp_repr@ */ | |
104 | 0, /* @tp_as_number@ */ | |
105 | 0, /* @tp_as_sequence@ */ | |
106 | 0, /* @tp_as_mapping@ */ | |
107 | 0, /* @tp_hash@ */ | |
108 | 0, /* @tp_call@ */ | |
109 | 0, /* @tp_str@ */ | |
110 | 0, /* @tp_getattro@ */ | |
111 | 0, /* @tp_setattro@ */ | |
112 | 0, /* @tp_as_buffer@ */ | |
113 | Py_TPFLAGS_DEFAULT | /* @tp_flags@ */ | |
114 | Py_TPFLAGS_BASETYPE, | |
115 | ||
116 | /* @tp_doc@ */ | |
31ff254e | 117 | "Iterates over the keys of a mapping.", |
10e6f88a MW |
118 | |
119 | 0, /* @tp_traverse@ */ | |
120 | 0, /* @tp_clear@ */ | |
121 | 0, /* @tp_richcompare@ */ | |
122 | 0, /* @tp_weaklistoffset@ */ | |
123 | PyObject_SelfIter, /* @tp_iter@ */ | |
31ff254e | 124 | keyiter_pynext, /* @tp_iternext@ */ |
10e6f88a MW |
125 | 0, /* @tp_methods@ */ |
126 | 0, /* @tp_members@ */ | |
127 | 0, /* @tp_getset@ */ | |
128 | 0, /* @tp_base@ */ | |
129 | 0, /* @tp_dict@ */ | |
130 | 0, /* @tp_descr_get@ */ | |
131 | 0, /* @tp_descr_set@ */ | |
132 | 0, /* @tp_dictoffset@ */ | |
133 | 0, /* @tp_init@ */ | |
134 | PyType_GenericAlloc, /* @tp_alloc@ */ | |
135 | abstract_pynew, /* @tp_new@ */ | |
136 | 0, /* @tp_free@ */ | |
137 | 0 /* @tp_is_gc@ */ | |
138 | }; | |
139 | ||
140 | static PyObject *valiter_pynext(PyObject *me) | |
141 | { | |
31ff254e MW |
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)); | |
10e6f88a | 145 | |
31ff254e MW |
146 | if (!e) return (0); |
147 | else return (gmops->entry_value(map, e)); | |
10e6f88a MW |
148 | } |
149 | ||
c263b05c | 150 | static const PyTypeObject valiter_pytype_skel = { |
591bf41b | 151 | PyVarObject_HEAD_INIT(0, 0) /* Header */ |
31ff254e | 152 | "_ValueIter", /* @tp_name@ */ |
10e6f88a MW |
153 | sizeof(iter_pyobj), /* @tp_basicsize@ */ |
154 | 0, /* @tp_itemsize@ */ | |
155 | ||
156 | iter_pydealloc, /* @tp_dealloc@ */ | |
157 | 0, /* @tp_print@ */ | |
158 | 0, /* @tp_getattr@ */ | |
159 | 0, /* @tp_setattr@ */ | |
160 | 0, /* @tp_compare@ */ | |
161 | 0, /* @tp_repr@ */ | |
162 | 0, /* @tp_as_number@ */ | |
163 | 0, /* @tp_as_sequence@ */ | |
164 | 0, /* @tp_as_mapping@ */ | |
165 | 0, /* @tp_hash@ */ | |
166 | 0, /* @tp_call@ */ | |
167 | 0, /* @tp_str@ */ | |
168 | 0, /* @tp_getattro@ */ | |
169 | 0, /* @tp_setattro@ */ | |
170 | 0, /* @tp_as_buffer@ */ | |
171 | Py_TPFLAGS_DEFAULT | /* @tp_flags@ */ | |
172 | Py_TPFLAGS_BASETYPE, | |
173 | ||
174 | /* @tp_doc@ */ | |
175 | "Iterates over the values of a mapping.", | |
176 | ||
177 | 0, /* @tp_traverse@ */ | |
178 | 0, /* @tp_clear@ */ | |
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@ */ | |
185 | 0, /* @tp_getset@ */ | |
186 | 0, /* @tp_base@ */ | |
187 | 0, /* @tp_dict@ */ | |
188 | 0, /* @tp_descr_get@ */ | |
189 | 0, /* @tp_descr_set@ */ | |
190 | 0, /* @tp_dictoffset@ */ | |
191 | 0, /* @tp_init@ */ | |
192 | PyType_GenericAlloc, /* @tp_alloc@ */ | |
193 | abstract_pynew, /* @tp_new@ */ | |
194 | 0, /* @tp_free@ */ | |
195 | 0 /* @tp_is_gc@ */ | |
196 | }; | |
197 | ||
31ff254e MW |
198 | static PyObject *itemiter_pynext(PyObject *me) |
199 | { | |
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)); | |
203 | PyObject *rc = 0; | |
204 | ||
205 | if (e) | |
206 | rc = Py_BuildValue("(NN)", | |
207 | gmops->entry_key(map, e), | |
208 | gmops->entry_value(map, e)); | |
209 | return (rc); | |
210 | } | |
211 | ||
212 | static const PyTypeObject itemiter_pytype_skel = { | |
591bf41b | 213 | PyVarObject_HEAD_INIT(0, 0) /* Header */ |
31ff254e MW |
214 | "_ItemIter", /* @tp_name@ */ |
215 | sizeof(iter_pyobj), /* @tp_basicsize@ */ | |
216 | 0, /* @tp_itemsize@ */ | |
217 | ||
218 | iter_pydealloc, /* @tp_dealloc@ */ | |
219 | 0, /* @tp_print@ */ | |
220 | 0, /* @tp_getattr@ */ | |
221 | 0, /* @tp_setattr@ */ | |
222 | 0, /* @tp_compare@ */ | |
223 | 0, /* @tp_repr@ */ | |
224 | 0, /* @tp_as_number@ */ | |
225 | 0, /* @tp_as_sequence@ */ | |
226 | 0, /* @tp_as_mapping@ */ | |
227 | 0, /* @tp_hash@ */ | |
228 | 0, /* @tp_call@ */ | |
229 | 0, /* @tp_str@ */ | |
230 | 0, /* @tp_getattro@ */ | |
231 | 0, /* @tp_setattro@ */ | |
232 | 0, /* @tp_as_buffer@ */ | |
233 | Py_TPFLAGS_DEFAULT | /* @tp_flags@ */ | |
234 | Py_TPFLAGS_BASETYPE, | |
235 | ||
236 | /* @tp_doc@ */ | |
237 | "Iterates over the items of a mapping.", | |
238 | ||
239 | 0, /* @tp_traverse@ */ | |
240 | 0, /* @tp_clear@ */ | |
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@ */ | |
247 | 0, /* @tp_getset@ */ | |
248 | 0, /* @tp_base@ */ | |
249 | 0, /* @tp_dict@ */ | |
250 | 0, /* @tp_descr_get@ */ | |
251 | 0, /* @tp_descr_set@ */ | |
252 | 0, /* @tp_dictoffset@ */ | |
253 | 0, /* @tp_init@ */ | |
254 | PyType_GenericAlloc, /* @tp_alloc@ */ | |
255 | abstract_pynew, /* @tp_new@ */ | |
256 | 0, /* @tp_free@ */ | |
257 | 0 /* @tp_is_gc@ */ | |
258 | }; | |
259 | ||
f7623015 MW |
260 | /*----- Mapping views -----------------------------------------------------*/ |
261 | ||
262 | #ifdef PY3 | |
263 | ||
264 | static PyTypeObject *keyview_pytype, *itemview_pytype, *valview_pytype; | |
265 | ||
266 | typedef struct view_pyobj { | |
267 | PyObject_HEAD | |
268 | PyObject *map; | |
269 | } view_pyobj; | |
270 | #define VIEW_MAP(o) (((view_pyobj *)(o))->map) | |
271 | ||
272 | static PyObject *gmap_mkview(PyObject *me, PyTypeObject *ty) | |
273 | { | |
274 | view_pyobj *v = PyObject_NEW(view_pyobj, ty); | |
275 | v->map = me; Py_INCREF(me); | |
276 | return ((PyObject *)v); | |
277 | } | |
278 | ||
279 | static void view_pydealloc(PyObject *me) | |
280 | { Py_DECREF(VIEW_MAP(me)); FREEOBJ(me); } | |
281 | ||
282 | #define BINOP(op, update) \ | |
283 | static PyObject *view_py##op(PyObject *me, PyObject *you) \ | |
284 | { \ | |
285 | PyObject *set = 0; \ | |
286 | PyObject *rc = 0; \ | |
287 | \ | |
288 | set = PySet_New(me); if (!set) goto end; \ | |
289 | if (!PyObject_CallMethod(set, #update, "(O)", you)) goto end; \ | |
290 | rc = set; set = 0; \ | |
291 | end: \ | |
292 | Py_XDECREF(set); \ | |
293 | return (rc); \ | |
294 | } | |
295 | BINOP(and, intersection_update) | |
296 | BINOP(or, update) | |
297 | BINOP(xor, symmetric_difference_update) | |
298 | #undef BINOP | |
299 | ||
300 | static int all_contained_p(PyObject *x, PyObject *y) | |
301 | { | |
302 | PyObject *i = 0, *e; | |
303 | int b, rc = -1; | |
304 | ||
305 | i = PyObject_GetIter(x); if (!i) goto end; | |
306 | for (;;) { | |
307 | e = PyIter_Next(i); if (!e) break; | |
308 | b = PySequence_Contains(y, e); Py_DECREF(e); if (b < 0) goto end; | |
309 | if (!b) { rc = 0; goto end; } | |
310 | } | |
311 | if (PyErr_Occurred()) goto end; | |
312 | rc = 1; | |
313 | end: | |
314 | Py_XDECREF(i); | |
315 | return (rc); | |
316 | } | |
317 | ||
318 | static Py_ssize_t view_pysize(PyObject *me) | |
319 | { return (PyMapping_Size(VIEW_MAP(me))); } | |
320 | ||
321 | static PyObject *view_pyrichcompare(PyObject *me, PyObject *you, int op) | |
322 | { | |
323 | PyObject *map = ITER_MAP(me); | |
324 | Py_ssize_t mysz, yoursz; | |
325 | int b; | |
326 | ||
327 | mysz = PyMapping_Size(map); if (mysz < 0) return (0); | |
328 | yoursz = PyObject_Size(you); | |
329 | if (yoursz < 0) { PyErr_Clear(); RETURN_NOTIMPL; } | |
330 | ||
331 | switch (op) { | |
332 | case Py_EQ: | |
333 | if (mysz != yoursz) RETURN_FALSE; | |
334 | b = all_contained_p(you, me); | |
335 | break; | |
336 | case Py_NE: | |
337 | if (mysz != yoursz) RETURN_TRUE; | |
338 | b = all_contained_p(you, me); | |
339 | break; | |
340 | case Py_LT: | |
341 | if (mysz >= yoursz) RETURN_FALSE; | |
342 | b = all_contained_p(me, you); | |
343 | break; | |
344 | case Py_LE: | |
345 | if (mysz > yoursz) RETURN_FALSE; | |
346 | b = all_contained_p(me, you); | |
347 | break; | |
348 | case Py_GE: | |
349 | if (mysz < yoursz) RETURN_FALSE; | |
350 | b = all_contained_p(you, me); | |
351 | break; | |
352 | case Py_GT: | |
353 | if (mysz <= yoursz) RETURN_FALSE; | |
354 | b = all_contained_p(you, me); | |
355 | break; | |
356 | default: | |
357 | abort(); | |
358 | } | |
359 | if (b < 0) return (0); | |
360 | return (getbool(b)); | |
361 | } | |
362 | ||
363 | static PyObject *keyview_pyiter(PyObject *me) | |
364 | { return (gmap_mkiter(VIEW_MAP(me), keyiter_pytype)); } | |
365 | ||
366 | static int keyview_pyhaskey(PyObject *me, PyObject *k) | |
367 | { | |
368 | PyObject *map = VIEW_MAP(me); | |
369 | const struct gmap_ops *gmops = GMAP_OPS(map); | |
370 | return (gmops->lookup(map, k, 0) ? 1 : PyErr_Occurred() ? -1 : 0); | |
371 | } | |
372 | ||
373 | static int itemview_pyhaskey(PyObject *me, PyObject *it) | |
374 | { | |
375 | PyObject *map = VIEW_MAP(me); | |
376 | const struct gmap_ops *gmops = GMAP_OPS(map); | |
377 | void *e; | |
378 | int b; | |
379 | PyObject *v; | |
380 | ||
381 | if (!PyTuple_Check(it) || PyTuple_GET_SIZE(it) != 2) return (0); | |
382 | e = gmops->lookup(map, PyTuple_GET_ITEM(it, 0), 0); | |
383 | if (!e) return (PyErr_Occurred() ? -1 : 0); | |
384 | v = gmops->entry_value(map, e); if (!v) return (-1); | |
385 | b = PyObject_RichCompareBool(v, PyTuple_GET_ITEM(it, 1), Py_EQ); | |
386 | Py_DECREF(v); return (b); | |
387 | } | |
388 | ||
389 | static PyObject *valview_pyiter(PyObject *me) | |
390 | { return (gmap_mkiter(VIEW_MAP(me), valiter_pytype)); } | |
391 | ||
392 | static PyObject *itemview_pyiter(PyObject *me) | |
393 | { return (gmap_mkiter(VIEW_MAP(me), itemiter_pytype)); } | |
394 | ||
395 | static const PyNumberMethods view_pynumber = { | |
396 | 0, /* @nb_add@ */ | |
397 | 0, /* @nb_subtract@ */ | |
398 | 0, /* @nb_multiply@ */ | |
399 | #ifdef PY2 | |
400 | 0, /* @nb_divide@ */ | |
401 | #endif | |
402 | 0, /* @nb_remainder@ */ | |
403 | 0, /* @nb_divmod@ */ | |
404 | 0, /* @nb_power@ */ | |
405 | 0, /* @nb_negative@ */ | |
406 | 0, /* @nb_positive@ */ | |
407 | 0, /* @nb_absolute@ */ | |
408 | 0, /* @nb_nonzero@ */ | |
409 | 0, /* @nb_invert@ */ | |
410 | 0, /* @nb_lshift@ */ | |
411 | 0, /* @nb_rshift@ */ | |
412 | view_pyand, /* @nb_and@ */ | |
413 | view_pyxor, /* @nb_xor@ */ | |
414 | view_pyor, /* @nb_or@ */ | |
415 | 0, /* @nb_coerce@ */ | |
416 | 0, /* @nb_int@ */ | |
417 | 0, /* @nb_long@ */ | |
418 | 0, /* @nb_float@ */ | |
419 | 0, /* @nb_oct@ */ | |
420 | 0, /* @nb_hex@ */ | |
421 | }; | |
422 | ||
423 | static const PySequenceMethods keyview_pysequence = { | |
424 | view_pysize, /* @sq_length@ */ | |
425 | 0, /* @sq_concat@ */ | |
426 | 0, /* @sq_repeat@ */ | |
427 | 0, /* @sq_item@ */ | |
428 | 0, /* @sq_slice@ */ | |
429 | 0, /* @sq_ass_item@ */ | |
430 | 0, /* @sq_ass_slice@ */ | |
431 | keyview_pyhaskey, /* @sq_contains@ */ | |
432 | 0, /* @sq_inplace_concat@ */ | |
433 | 0, /* @sq_inplace_repeat@ */ | |
434 | }; | |
435 | ||
436 | static const PyTypeObject keyview_pytype_skel = { | |
437 | PyVarObject_HEAD_INIT(0, 0) /* Header */ | |
438 | "_KeyView", /* @tp_name@ */ | |
439 | sizeof(view_pyobj), /* @tp_basicsize@ */ | |
440 | 0, /* @tp_itemsize@ */ | |
441 | ||
442 | view_pydealloc, /* @tp_dealloc@ */ | |
443 | 0, /* @tp_print@ */ | |
444 | 0, /* @tp_getattr@ */ | |
445 | 0, /* @tp_setattr@ */ | |
446 | 0, /* @tp_compare@ */ | |
447 | 0, /* @tp_repr@ */ | |
448 | PYNUMBER(view), /* @tp_as_number@ */ | |
449 | PYSEQUENCE(keyview), /* @tp_as_sequence@ */ | |
450 | 0, /* @tp_as_mapping@ */ | |
451 | 0, /* @tp_hash@ */ | |
452 | 0, /* @tp_call@ */ | |
453 | 0, /* @tp_str@ */ | |
454 | 0, /* @tp_getattro@ */ | |
455 | 0, /* @tp_setattro@ */ | |
456 | 0, /* @tp_as_buffer@ */ | |
457 | Py_TPFLAGS_DEFAULT | /* @tp_flags@ */ | |
458 | Py_TPFLAGS_BASETYPE, | |
459 | ||
460 | /* @tp_doc@ */ | |
461 | "View of a mapping's keys.", | |
462 | ||
463 | 0, /* @tp_traverse@ */ | |
464 | 0, /* @tp_clear@ */ | |
465 | view_pyrichcompare, /* @tp_richcompare@ */ | |
466 | 0, /* @tp_weaklistoffset@ */ | |
467 | keyview_pyiter, /* @tp_iter@ */ | |
468 | 0, /* @tp_iternext@ */ | |
469 | 0, /* @tp_methods@ */ | |
470 | 0, /* @tp_members@ */ | |
471 | 0, /* @tp_getset@ */ | |
472 | 0, /* @tp_base@ */ | |
473 | 0, /* @tp_dict@ */ | |
474 | 0, /* @tp_descr_get@ */ | |
475 | 0, /* @tp_descr_set@ */ | |
476 | 0, /* @tp_dictoffset@ */ | |
477 | 0, /* @tp_init@ */ | |
478 | PyType_GenericAlloc, /* @tp_alloc@ */ | |
479 | abstract_pynew, /* @tp_new@ */ | |
480 | 0, /* @tp_free@ */ | |
481 | 0 /* @tp_is_gc@ */ | |
482 | }; | |
483 | ||
484 | static const PySequenceMethods valview_pysequence = { | |
485 | view_pysize, /* @sq_length@ */ | |
486 | 0, /* @sq_concat@ */ | |
487 | 0, /* @sq_repeat@ */ | |
488 | 0, /* @sq_item@ */ | |
489 | 0, /* @sq_slice@ */ | |
490 | 0, /* @sq_ass_item@ */ | |
491 | 0, /* @sq_ass_slice@ */ | |
492 | 0, /* @sq_contains@ */ | |
493 | 0, /* @sq_inplace_concat@ */ | |
494 | 0, /* @sq_inplace_repeat@ */ | |
495 | }; | |
496 | ||
497 | static const PyTypeObject valview_pytype_skel = { | |
498 | PyVarObject_HEAD_INIT(0, 0) /* Header */ | |
499 | "_ValueView", /* @tp_name@ */ | |
500 | sizeof(view_pyobj), /* @tp_basicsize@ */ | |
501 | 0, /* @tp_itemsize@ */ | |
502 | ||
503 | view_pydealloc, /* @tp_dealloc@ */ | |
504 | 0, /* @tp_print@ */ | |
505 | 0, /* @tp_getattr@ */ | |
506 | 0, /* @tp_setattr@ */ | |
507 | 0, /* @tp_compare@ */ | |
508 | 0, /* @tp_repr@ */ | |
509 | PYNUMBER(view), /* @tp_as_number@ */ | |
510 | PYSEQUENCE(valview), /* @tp_as_sequence@ */ | |
511 | 0, /* @tp_as_mapping@ */ | |
512 | 0, /* @tp_hash@ */ | |
513 | 0, /* @tp_call@ */ | |
514 | 0, /* @tp_str@ */ | |
515 | 0, /* @tp_getattro@ */ | |
516 | 0, /* @tp_setattro@ */ | |
517 | 0, /* @tp_as_buffer@ */ | |
518 | Py_TPFLAGS_DEFAULT | /* @tp_flags@ */ | |
519 | Py_TPFLAGS_BASETYPE, | |
520 | ||
521 | /* @tp_doc@ */ | |
522 | "View of a mapping's values.", | |
523 | ||
524 | 0, /* @tp_traverse@ */ | |
525 | 0, /* @tp_clear@ */ | |
526 | 0, /* @tp_richcompare@ */ | |
527 | 0, /* @tp_weaklistoffset@ */ | |
528 | valview_pyiter, /* @tp_iter@ */ | |
529 | 0, /* @tp_iternext@ */ | |
530 | 0, /* @tp_methods@ */ | |
531 | 0, /* @tp_members@ */ | |
532 | 0, /* @tp_getset@ */ | |
533 | 0, /* @tp_base@ */ | |
534 | 0, /* @tp_dict@ */ | |
535 | 0, /* @tp_descr_get@ */ | |
536 | 0, /* @tp_descr_set@ */ | |
537 | 0, /* @tp_dictoffset@ */ | |
538 | 0, /* @tp_init@ */ | |
539 | PyType_GenericAlloc, /* @tp_alloc@ */ | |
540 | abstract_pynew, /* @tp_new@ */ | |
541 | 0, /* @tp_free@ */ | |
542 | 0 /* @tp_is_gc@ */ | |
543 | }; | |
544 | ||
545 | static const PySequenceMethods itemview_pysequence = { | |
546 | view_pysize, /* @sq_length@ */ | |
547 | 0, /* @sq_concat@ */ | |
548 | 0, /* @sq_repeat@ */ | |
549 | 0, /* @sq_item@ */ | |
550 | 0, /* @sq_slice@ */ | |
551 | 0, /* @sq_ass_item@ */ | |
552 | 0, /* @sq_ass_slice@ */ | |
553 | itemview_pyhaskey, /* @sq_contains@ */ | |
554 | 0, /* @sq_inplace_concat@ */ | |
555 | 0, /* @sq_inplace_repeat@ */ | |
556 | }; | |
557 | ||
558 | static const PyTypeObject itemview_pytype_skel = { | |
559 | PyVarObject_HEAD_INIT(0, 0) /* Header */ | |
560 | "_ItemView", /* @tp_name@ */ | |
561 | sizeof(view_pyobj), /* @tp_basicsize@ */ | |
562 | 0, /* @tp_itemsize@ */ | |
563 | ||
564 | view_pydealloc, /* @tp_dealloc@ */ | |
565 | 0, /* @tp_print@ */ | |
566 | 0, /* @tp_getattr@ */ | |
567 | 0, /* @tp_setattr@ */ | |
568 | 0, /* @tp_compare@ */ | |
569 | 0, /* @tp_repr@ */ | |
570 | PYNUMBER(view), /* @tp_as_number@ */ | |
571 | PYSEQUENCE(itemview), /* @tp_as_sequence@ */ | |
572 | 0, /* @tp_as_mapping@ */ | |
573 | 0, /* @tp_hash@ */ | |
574 | 0, /* @tp_call@ */ | |
575 | 0, /* @tp_str@ */ | |
576 | 0, /* @tp_getattro@ */ | |
577 | 0, /* @tp_setattro@ */ | |
578 | 0, /* @tp_as_buffer@ */ | |
579 | Py_TPFLAGS_DEFAULT | /* @tp_flags@ */ | |
580 | Py_TPFLAGS_BASETYPE, | |
581 | ||
582 | /* @tp_doc@ */ | |
583 | "View of a mapping's key/value items.", | |
584 | ||
585 | 0, /* @tp_traverse@ */ | |
586 | 0, /* @tp_clear@ */ | |
587 | view_pyrichcompare, /* @tp_richcompare@ */ | |
588 | 0, /* @tp_weaklistoffset@ */ | |
589 | itemview_pyiter, /* @tp_iter@ */ | |
590 | 0, /* @tp_iternext@ */ | |
591 | 0, /* @tp_methods@ */ | |
592 | 0, /* @tp_members@ */ | |
593 | 0, /* @tp_getset@ */ | |
594 | 0, /* @tp_base@ */ | |
595 | 0, /* @tp_dict@ */ | |
596 | 0, /* @tp_descr_get@ */ | |
597 | 0, /* @tp_descr_set@ */ | |
598 | 0, /* @tp_dictoffset@ */ | |
599 | 0, /* @tp_init@ */ | |
600 | PyType_GenericAlloc, /* @tp_alloc@ */ | |
601 | abstract_pynew, /* @tp_new@ */ | |
602 | 0, /* @tp_free@ */ | |
603 | 0 /* @tp_is_gc@ */ | |
604 | }; | |
605 | ||
606 | #endif | |
607 | ||
31ff254e MW |
608 | /*----- Other mapping protocol support ------------------------------------*/ |
609 | ||
610 | Py_ssize_t gmap_pysize(PyObject *me) | |
611 | { | |
612 | const gmap_ops *gmops = GMAP_OPS(me); | |
613 | union iterstate iter; | |
614 | void *i; | |
615 | Py_ssize_t n = 0; | |
616 | ||
617 | i = iter_init(me, &iter); | |
618 | while (gmops->iter_next(me, i)) n++; | |
619 | iter_free(me, &iter); | |
620 | return (n); | |
621 | } | |
622 | ||
623 | PyObject *gmap_pylookup(PyObject *me, PyObject *key) | |
624 | { | |
625 | const gmap_ops *gmops = GMAP_OPS(me); | |
626 | void *e = gmops->lookup(me, key, 0); | |
627 | PyObject *rc = 0; | |
628 | ||
629 | if (!e) { if (!PyErr_Occurred()) MAPERR(key); else goto end; } | |
630 | rc = gmops->entry_value(me, e); | |
631 | end: | |
632 | return (rc); | |
633 | } | |
634 | ||
635 | int gmap_pystore(PyObject *me, PyObject *key, PyObject *value) | |
636 | { | |
637 | const gmap_ops *gmops = GMAP_OPS(me); | |
638 | unsigned f; | |
639 | void *e = gmops->lookup(me, key, &f); | |
640 | int rc = -1; | |
641 | ||
642 | if (!e) goto end; | |
643 | if (!value) | |
644 | rc = gmops->del_entry(me, e); | |
645 | else { | |
646 | rc = gmops->set_entry(me, e, value); | |
647 | if (rc && !f) gmops->del_entry(me, e); | |
648 | } | |
649 | rc = 0; | |
650 | end: | |
651 | return (rc); | |
652 | } | |
653 | ||
654 | int gmap_pyhaskey(PyObject *me, PyObject *key) | |
655 | { return (GMAP_OPS(me)->lookup(me, key, 0) ? 1 : PyErr_Occurred() ? -1 : 0); } | |
656 | ||
c90f712e | 657 | const PySequenceMethods gmap_pysequence = { |
10e6f88a MW |
658 | 0, /* @sq_length@ */ |
659 | 0, /* @sq_concat@ */ | |
660 | 0, /* @sq_repeat@ */ | |
661 | 0, /* @sq_item@ */ | |
662 | 0, /* @sq_slice@ */ | |
663 | 0, /* @sq_ass_item@ */ | |
664 | 0, /* @sq_ass_slice@ */ | |
31ff254e | 665 | gmap_pyhaskey, /* @sq_contains@ */ |
10e6f88a MW |
666 | 0, /* @sq_inplace_concat@ */ |
667 | 0 /* @sq_inplace_repeat@ */ | |
668 | }; | |
669 | ||
f7623015 MW |
670 | #ifdef PY3 |
671 | ||
672 | PyObject *gmapmeth_keys(PyObject *me) | |
673 | { return (gmap_mkview(me, keyview_pytype)); } | |
674 | ||
675 | PyObject *gmapmeth_values(PyObject *me) | |
676 | { return (gmap_mkview(me, valview_pytype)); } | |
677 | ||
678 | PyObject *gmapmeth_items(PyObject *me) | |
679 | { return (gmap_mkview(me, itemview_pytype)); } | |
680 | ||
681 | #else | |
682 | ||
10e6f88a MW |
683 | PyObject *gmapmeth_has_key(PyObject *me, PyObject *arg) |
684 | { | |
685 | PyObject *k; | |
31ff254e | 686 | void *e; |
10e6f88a | 687 | if (!PyArg_ParseTuple(arg, "O:has_key", &k)) return (0); |
31ff254e MW |
688 | e = GMAP_OPS(me)->lookup(me, k, 0); |
689 | if (e) RETURN_TRUE; | |
690 | else if (!PyErr_Occurred()) RETURN_FALSE; | |
691 | else return (0); | |
10e6f88a MW |
692 | } |
693 | ||
91e56f06 | 694 | PyObject *gmapmeth_keys(PyObject *me) |
10e6f88a | 695 | { |
31ff254e MW |
696 | const gmap_ops *gmops = GMAP_OPS(me); |
697 | union iterstate iter; void *i = 0, *e; | |
698 | PyObject *l = 0, *k, *rc = 0; | |
10e6f88a MW |
699 | int err; |
700 | ||
31ff254e MW |
701 | if ((l = PyList_New(0)) == 0) goto done; |
702 | i = iter_init(me, &iter); | |
703 | while ((e = gmops->iter_next(me, i)) != 0) { | |
704 | k = gmops->entry_key(me, e); | |
705 | err = PyList_Append(l, k); | |
706 | Py_DECREF(k); | |
707 | if (err) goto done; | |
708 | } | |
10e6f88a MW |
709 | rc = l; l = 0; |
710 | done: | |
31ff254e MW |
711 | Py_XDECREF(l); |
712 | if (i) iter_free(me, &iter); | |
10e6f88a MW |
713 | return (rc); |
714 | } | |
715 | ||
91e56f06 | 716 | PyObject *gmapmeth_values(PyObject *me) |
10e6f88a | 717 | { |
31ff254e MW |
718 | const gmap_ops *gmops = GMAP_OPS(me); |
719 | union iterstate iter; void *i = 0, *e; | |
720 | PyObject *l = 0, *v, *rc = 0; | |
721 | int err; | |
722 | ||
723 | if ((l = PyList_New(0)) == 0) goto done; | |
724 | i = iter_init(me, &iter); | |
725 | while ((e = gmops->iter_next(me, i)) != 0) { | |
726 | v = gmops->entry_value(me, e); | |
727 | err = PyList_Append(l, v); | |
728 | Py_DECREF(v); | |
10e6f88a MW |
729 | if (err) goto done; |
730 | } | |
10e6f88a MW |
731 | rc = l; l = 0; |
732 | done: | |
31ff254e MW |
733 | Py_XDECREF(l); |
734 | if (i) iter_free(me, &iter); | |
10e6f88a MW |
735 | return (rc); |
736 | } | |
737 | ||
91e56f06 | 738 | PyObject *gmapmeth_items(PyObject *me) |
10e6f88a | 739 | { |
31ff254e MW |
740 | const gmap_ops *gmops = GMAP_OPS(me); |
741 | union iterstate iter; void *i = 0, *e; | |
742 | PyObject *l = 0, *z, *rc = 0; | |
743 | int err; | |
744 | ||
745 | if ((l = PyList_New(0)) == 0) goto done; | |
746 | i = iter_init(me, &iter); | |
747 | while ((e = gmops->iter_next(me, i)) != 0) { | |
748 | if ((z = Py_BuildValue("(NN)", | |
749 | gmops->entry_key(me, e), | |
750 | gmops->entry_value(me, e))) == 0) | |
751 | goto done; | |
752 | err = PyList_Append(l, z); | |
753 | Py_XDECREF(z); | |
10e6f88a MW |
754 | if (err) goto done; |
755 | } | |
10e6f88a MW |
756 | rc = l; l = 0; |
757 | done: | |
31ff254e MW |
758 | Py_XDECREF(l); |
759 | if (i) iter_free(me, &iter); | |
10e6f88a MW |
760 | return (rc); |
761 | } | |
762 | ||
91e56f06 | 763 | PyObject *gmapmeth_iterkeys(PyObject *me) |
31ff254e | 764 | { return (gmap_mkiter(me, keyiter_pytype)); } |
10e6f88a | 765 | |
91e56f06 | 766 | PyObject *gmapmeth_itervalues(PyObject *me) |
31ff254e | 767 | { return (gmap_mkiter(me, valiter_pytype)); } |
10e6f88a | 768 | |
91e56f06 | 769 | PyObject *gmapmeth_iteritems(PyObject *me) |
31ff254e | 770 | { return (gmap_mkiter(me, itemiter_pytype)); } |
10e6f88a | 771 | |
f7623015 MW |
772 | #endif |
773 | ||
31ff254e MW |
774 | PyObject *gmap_pyiter(PyObject *me) |
775 | { return gmap_mkiter(me, keyiter_pytype); } | |
10e6f88a | 776 | |
91e56f06 | 777 | PyObject *gmapmeth_clear(PyObject *me) |
10e6f88a | 778 | { |
31ff254e MW |
779 | const gmap_ops *gmops = GMAP_OPS(me); |
780 | union iterstate iter; | |
781 | void *i, *e; | |
782 | PyObject *rc = 0; | |
783 | ||
784 | i = iter_init(me, &iter); | |
785 | for (;;) { | |
786 | e = gmops->iter_next(me, i); if (!e) break; | |
787 | if (gmops->del_entry(me, e)) goto end; | |
10e6f88a | 788 | } |
31ff254e | 789 | iter_free(me, &iter); |
10e6f88a MW |
790 | rc = me; Py_INCREF(me); |
791 | end: | |
10e6f88a MW |
792 | return (rc); |
793 | } | |
794 | ||
795 | static const char *const def_kwlist[] = { "key", "default", 0 }; | |
f7160cff | 796 | #define DEF_KWLIST ((/*unconst*/ char **)def_kwlist) |
10e6f88a MW |
797 | |
798 | PyObject *gmapmeth_get(PyObject *me, PyObject *arg, PyObject *kw) | |
799 | { | |
31ff254e MW |
800 | const gmap_ops *gmops = GMAP_OPS(me); |
801 | PyObject *k, *def = Py_None; | |
802 | void *e; | |
10e6f88a | 803 | |
f7160cff | 804 | if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:get", DEF_KWLIST, &k, &def)) |
10e6f88a | 805 | return (0); |
31ff254e MW |
806 | e = gmops->lookup(me, k, 0); |
807 | if (e) return (gmops->entry_value(me, e)); | |
808 | else if (!PyErr_Occurred()) RETURN_OBJ(def); | |
809 | else return (0); | |
10e6f88a MW |
810 | } |
811 | ||
812 | PyObject *gmapmeth_setdefault(PyObject *me, PyObject *arg, PyObject *kw) | |
813 | { | |
31ff254e MW |
814 | const gmap_ops *gmops = GMAP_OPS(me); |
815 | PyObject *k, *def = Py_None; | |
816 | void *e; | |
817 | unsigned f; | |
10e6f88a | 818 | |
f7160cff | 819 | if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:setdefault", DEF_KWLIST, |
10e6f88a MW |
820 | &k, &def)) |
821 | return (0); | |
31ff254e MW |
822 | e = gmops->lookup(me, k, &f); |
823 | if (!e) return (0); | |
824 | else if (f) return (gmops->entry_value(me, e)); | |
825 | else if (gmops->set_entry(me, e, def)) return (0); | |
826 | else RETURN_OBJ(def); | |
10e6f88a MW |
827 | } |
828 | ||
829 | PyObject *gmapmeth_pop(PyObject *me, PyObject *arg, PyObject *kw) | |
830 | { | |
31ff254e MW |
831 | const gmap_ops *gmops = GMAP_OPS(me); |
832 | PyObject *k, *def = 0; | |
833 | PyObject *rc = 0; | |
834 | void *e; | |
10e6f88a | 835 | |
f7160cff | 836 | if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:pop", DEF_KWLIST, &k, &def)) |
31ff254e MW |
837 | goto end; |
838 | e = gmops->lookup(me, k, 0); | |
839 | if (!e) { | |
840 | if (PyErr_Occurred()) goto end; | |
841 | else if (def) { rc = def; Py_INCREF(rc); } | |
842 | else MAPERR(k); | |
843 | } else { | |
844 | rc = gmops->entry_value(me, e); | |
845 | if (gmops->del_entry(me, e)) { Py_DECREF(rc); rc = 0; } | |
846 | } | |
847 | end: | |
848 | return (rc); | |
10e6f88a MW |
849 | } |
850 | ||
31ff254e | 851 | static int update_core(PyObject *me, PyObject *map) |
10e6f88a | 852 | { |
31ff254e MW |
853 | const gmap_ops *gmops = GMAP_OPS(me); |
854 | PyObject *i = 0, *item = 0, *k = 0, *v = 0; | |
855 | void *e; | |
856 | unsigned foundp; | |
857 | int rc = -1; | |
858 | ||
f7623015 MW |
859 | v = PyObject_CallMethod(map, PY23("iteritems", "items"), 0); |
860 | #ifdef PY3 | |
861 | if (v) { i = PyObject_GetIter(v); Py_DECREF(v); v = 0; } | |
862 | #else | |
6286102d | 863 | i = v; v = 0; |
f7623015 | 864 | #endif |
31ff254e MW |
865 | |
866 | if (i) { | |
867 | for (;;) { | |
868 | item = PyIter_Next(i); if (!item) break; | |
869 | if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 2) | |
870 | TYERR("wanted a pair"); | |
871 | k = PyTuple_GET_ITEM(item, 0); Py_INCREF(k); | |
872 | v = PyTuple_GET_ITEM(item, 1); Py_INCREF(v); | |
873 | e = gmops->lookup(me, k, &foundp); if (!e) goto end; | |
874 | if (gmops->set_entry(me, e, v)) goto end; | |
875 | Py_DECREF(item); Py_DECREF(k); Py_DECREF(v); item = k = v = 0; | |
876 | } | |
877 | if (PyErr_Occurred()) goto end; | |
878 | } else { | |
879 | PyErr_Clear(); | |
880 | i = PyObject_GetIter(map); if (!i) goto end; | |
881 | for (;;) { | |
882 | k = PyIter_Next(i); if (!k) goto end; | |
883 | v = PyObject_GetItem(map, k); if (!v) goto end; | |
884 | e = gmops->lookup(me, k, &foundp); if (!e) goto end; | |
885 | if (gmops->set_entry(me, e, v)) goto end; | |
886 | Py_DECREF(k); Py_DECREF(v); k = v = 0; | |
887 | } | |
888 | if (PyErr_Occurred()) goto end; | |
10e6f88a | 889 | } |
31ff254e | 890 | rc = 0; |
10e6f88a | 891 | end: |
31ff254e MW |
892 | Py_XDECREF(i); Py_XDECREF(item); |
893 | Py_XDECREF(k); Py_XDECREF(v); | |
10e6f88a MW |
894 | return (rc); |
895 | } | |
896 | ||
31ff254e | 897 | PyObject *gmapmeth_update(PyObject *me, PyObject *arg, PyObject *kw) |
10e6f88a | 898 | { |
31ff254e | 899 | PyObject *map = 0; |
10e6f88a | 900 | |
31ff254e MW |
901 | if (!PyArg_ParseTuple(arg, "|O:update", &map)) return (0); |
902 | if (map && update_core(me, map)) return (0); | |
903 | if (kw && update_core(me, kw)) return (0); | |
904 | RETURN_ME; | |
905 | } | |
906 | ||
907 | PyObject *gmapmeth_popitem(PyObject *me) | |
908 | { | |
909 | const gmap_ops *gmops = GMAP_OPS(me); | |
910 | union iterstate iter; | |
911 | void *i; | |
912 | PyObject *rc = 0; | |
913 | void *e; | |
914 | ||
915 | i = iter_init(me, &iter); | |
916 | e = gmops->iter_next(me, i); | |
917 | iter_free(me, &iter); | |
918 | if (!e) | |
919 | MAPERR(Py_None); | |
920 | else { | |
921 | rc = Py_BuildValue("(NN)", | |
922 | gmops->entry_key(me, e), gmops->entry_value(me, e)); | |
923 | if (gmops->del_entry(me, e)) { Py_DECREF(rc); rc = 0; } | |
10e6f88a | 924 | } |
10e6f88a | 925 | end: |
10e6f88a MW |
926 | return (rc); |
927 | } | |
928 | ||
31ff254e MW |
929 | const PyMethodDef gmapro_pymethods[] = { |
930 | GMAP_ROMETHODS | |
931 | { 0 } | |
932 | }; | |
933 | ||
c90f712e | 934 | const PyMethodDef gmap_pymethods[] = { |
10e6f88a MW |
935 | GMAP_METHODS |
936 | { 0 } | |
937 | }; | |
938 | ||
939 | /*----- Submodule initialization ------------------------------------------*/ | |
940 | ||
941 | void pyke_gmap_pyinit(void) | |
942 | { | |
31ff254e | 943 | INITTYPE(keyiter, root); |
10e6f88a MW |
944 | INITTYPE(itemiter, root); |
945 | INITTYPE(valiter, root); | |
f7623015 MW |
946 | #ifdef PY3 |
947 | INITTYPE(keyview, root); | |
948 | INITTYPE(valview, root); | |
949 | INITTYPE(itemview, root); | |
950 | #endif | |
10e6f88a MW |
951 | } |
952 | ||
953 | void pyke_gmap_pyinsert(PyObject *mod) | |
954 | { | |
31ff254e MW |
955 | INSERT("_KeyIter", keyiter_pytype); |
956 | INSERT("_ValueIter", valiter_pytype); | |
957 | INSERT("_ItemIter", itemiter_pytype); | |
f7623015 MW |
958 | #ifdef PY3 |
959 | INSERT("_KeyView", keyview_pytype); | |
960 | INSERT("_ValueView", valview_pytype); | |
961 | INSERT("_ItemView", itemview_pytype); | |
962 | #endif | |
10e6f88a MW |
963 | } |
964 | ||
965 | /*----- That's all, folks -------------------------------------------------*/ |