chiark / gitweb /
pyke/pyke-mLib.c: Raise `OverflowError' on out-of-range inputs.
[mLib-python] / 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 -------------------------------------------------*/