pyke/pyke-mLib.c: Raise `OverflowError' on out-of-range inputs.
[pyke] / mapping.c
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
33 static PyTypeObject *keyiter_pytype, *itemiter_pytype, *valiter_pytype;
34
35 union iterstate {
36 void *external;
37 void *internal[4];
38 };
39
40 typedef struct iter_pyobj {
41 PyObject_HEAD
42 PyObject *map;
43 union iterstate iter;
44 } 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)
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); }
65
66 static void iter_pydealloc(PyObject *me)
67 {
68 PyObject *map = ITER_MAP(me);
69 iter_free(map, ITER_ITER(me));
70 Py_DECREF(map); FREEOBJ(me);
71 }
72
73 static PyObject *gmap_mkiter(PyObject *me, PyTypeObject *ty)
74 {
75 iter_pyobj *iter = PyObject_NEW(iter_pyobj, ty);
76
77 iter->map = me; Py_INCREF(me);
78 iter_init(me, &iter->iter);
79 return ((PyObject *)iter);
80 }
81
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 = {
93 PyVarObject_HEAD_INIT(0, 0) /* Header */
94 "_KeyIter", /* @tp_name@ */
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@ */
117 "Iterates over the keys of a mapping.",
118
119 0, /* @tp_traverse@ */
120 0, /* @tp_clear@ */
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@ */
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 {
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));
145
146 if (!e) return (0);
147 else return (gmops->entry_value(map, e));
148 }
149
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@ */
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
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 = {
213 PyVarObject_HEAD_INIT(0, 0) /* Header */
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
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
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
657 const PySequenceMethods gmap_pysequence = {
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@ */
665 gmap_pyhaskey, /* @sq_contains@ */
666 0, /* @sq_inplace_concat@ */
667 0 /* @sq_inplace_repeat@ */
668 };
669
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
683 PyObject *gmapmeth_has_key(PyObject *me, PyObject *arg)
684 {
685 PyObject *k;
686 void *e;
687 if (!PyArg_ParseTuple(arg, "O:has_key", &k)) return (0);
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);
692 }
693
694 PyObject *gmapmeth_keys(PyObject *me)
695 {
696 const gmap_ops *gmops = GMAP_OPS(me);
697 union iterstate iter; void *i = 0, *e;
698 PyObject *l = 0, *k, *rc = 0;
699 int err;
700
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 }
709 rc = l; l = 0;
710 done:
711 Py_XDECREF(l);
712 if (i) iter_free(me, &iter);
713 return (rc);
714 }
715
716 PyObject *gmapmeth_values(PyObject *me)
717 {
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);
729 if (err) goto done;
730 }
731 rc = l; l = 0;
732 done:
733 Py_XDECREF(l);
734 if (i) iter_free(me, &iter);
735 return (rc);
736 }
737
738 PyObject *gmapmeth_items(PyObject *me)
739 {
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);
754 if (err) goto done;
755 }
756 rc = l; l = 0;
757 done:
758 Py_XDECREF(l);
759 if (i) iter_free(me, &iter);
760 return (rc);
761 }
762
763 PyObject *gmapmeth_iterkeys(PyObject *me)
764 { return (gmap_mkiter(me, keyiter_pytype)); }
765
766 PyObject *gmapmeth_itervalues(PyObject *me)
767 { return (gmap_mkiter(me, valiter_pytype)); }
768
769 PyObject *gmapmeth_iteritems(PyObject *me)
770 { return (gmap_mkiter(me, itemiter_pytype)); }
771
772 #endif
773
774 PyObject *gmap_pyiter(PyObject *me)
775 { return gmap_mkiter(me, keyiter_pytype); }
776
777 PyObject *gmapmeth_clear(PyObject *me)
778 {
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;
788 }
789 iter_free(me, &iter);
790 rc = me; Py_INCREF(me);
791 end:
792 return (rc);
793 }
794
795 static const char *const def_kwlist[] = { "key", "default", 0 };
796 #define DEF_KWLIST ((/*unconst*/ char **)def_kwlist)
797
798 PyObject *gmapmeth_get(PyObject *me, PyObject *arg, PyObject *kw)
799 {
800 const gmap_ops *gmops = GMAP_OPS(me);
801 PyObject *k, *def = Py_None;
802 void *e;
803
804 if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:get", DEF_KWLIST, &k, &def))
805 return (0);
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);
810 }
811
812 PyObject *gmapmeth_setdefault(PyObject *me, PyObject *arg, PyObject *kw)
813 {
814 const gmap_ops *gmops = GMAP_OPS(me);
815 PyObject *k, *def = Py_None;
816 void *e;
817 unsigned f;
818
819 if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:setdefault", DEF_KWLIST,
820 &k, &def))
821 return (0);
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);
827 }
828
829 PyObject *gmapmeth_pop(PyObject *me, PyObject *arg, PyObject *kw)
830 {
831 const gmap_ops *gmops = GMAP_OPS(me);
832 PyObject *k, *def = 0;
833 PyObject *rc = 0;
834 void *e;
835
836 if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:pop", DEF_KWLIST, &k, &def))
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);
849 }
850
851 static int update_core(PyObject *me, PyObject *map)
852 {
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
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
863 i = v; v = 0;
864 #endif
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;
889 }
890 rc = 0;
891 end:
892 Py_XDECREF(i); Py_XDECREF(item);
893 Py_XDECREF(k); Py_XDECREF(v);
894 return (rc);
895 }
896
897 PyObject *gmapmeth_update(PyObject *me, PyObject *arg, PyObject *kw)
898 {
899 PyObject *map = 0;
900
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; }
924 }
925 end:
926 return (rc);
927 }
928
929 const PyMethodDef gmapro_pymethods[] = {
930 GMAP_ROMETHODS
931 { 0 }
932 };
933
934 const PyMethodDef gmap_pymethods[] = {
935 GMAP_METHODS
936 { 0 }
937 };
938
939 /*----- Submodule initialization ------------------------------------------*/
940
941 void pyke_gmap_pyinit(void)
942 {
943 INITTYPE(keyiter, root);
944 INITTYPE(itemiter, root);
945 INITTYPE(valiter, root);
946 #ifdef PY3
947 INITTYPE(keyview, root);
948 INITTYPE(valview, root);
949 INITTYPE(itemview, root);
950 #endif
951 }
952
953 void pyke_gmap_pyinsert(PyObject *mod)
954 {
955 INSERT("_KeyIter", keyiter_pytype);
956 INSERT("_ValueIter", valiter_pytype);
957 INSERT("_ItemIter", itemiter_pytype);
958 #ifdef PY3
959 INSERT("_KeyView", keyview_pytype);
960 INSERT("_ValueView", valview_pytype);
961 INSERT("_ItemView", itemview_pytype);
962 #endif
963 }
964
965 /*----- That's all, folks -------------------------------------------------*/