1 #ifndef _MULTIDICT_PAIR_LIST_H
2 #define _MULTIDICT_PAIR_LIST_H
3 
4 #ifdef __cplusplus
5 extern "C" {
6 #endif
7 
8 #include <string.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 
12 typedef PyObject * (*calc_identity_func)(PyObject *key);
13 
14 typedef struct pair {
15     PyObject  *identity;  // 8
16     PyObject  *key;       // 8
17     PyObject  *value;     // 8
18     Py_hash_t  hash;      // 8
19 } pair_t;
20 
21 /* Note about the structure size
22 With 29 pairs the MultiDict object size is slightly less than 1KiB
23 (1000-1008 bytes depending on Python version,
24 plus extra 12 bytes for memory allocator internal structures).
25 As the result the max reserved size is 1020 bytes at most.
26 
27 To fit into 512 bytes, the structure can contain only 13 pairs
28 which is too small, e.g. https://www.python.org returns 16 headers
29 (9 of them are caching proxy information though).
30 
31 The embedded buffer intention is to fit the vast majority of possible
32 HTTP headers into the buffer without allocating an extra memory block.
33 */
34 
35 #if (PY_VERSION_HEX < 0x03080000)
36 #define EMBEDDED_CAPACITY 28
37 #else
38 #define EMBEDDED_CAPACITY 29
39 #endif
40 
41 typedef struct pair_list {  // 40
42     Py_ssize_t  capacity;   // 8
43     Py_ssize_t  size;       // 8
44     uint64_t  version;      // 8
45     calc_identity_func calc_identity;  // 8
46     pair_t *pairs;          // 8
47     pair_t buffer[EMBEDDED_CAPACITY];
48 } pair_list_t;
49 
50 #define MIN_CAPACITY 63
51 #define CAPACITY_STEP 64
52 
53 /* Global counter used to set ma_version_tag field of dictionary.
54  * It is incremented each time that a dictionary is created and each
55  * time that a dictionary is modified. */
56 static uint64_t pair_list_global_version = 0;
57 
58 #define NEXT_VERSION() (++pair_list_global_version)
59 
60 
61 static inline int
str_cmp(PyObject * s1,PyObject * s2)62 str_cmp(PyObject *s1, PyObject *s2)
63 {
64     PyObject *ret = PyUnicode_RichCompare(s1, s2, Py_EQ);
65     if (ret == Py_True) {
66         Py_DECREF(ret);
67         return 1;
68     }
69     else if (ret == NULL) {
70         return -1;
71     }
72     else {
73         Py_DECREF(ret);
74         return 0;
75     }
76 }
77 
78 
79 static inline PyObject *
key_to_str(PyObject * key)80 key_to_str(PyObject *key)
81 {
82     PyObject *ret;
83     PyTypeObject *type = Py_TYPE(key);
84     if (type == &istr_type) {
85         ret = ((istrobject*)key)->canonical;
86         Py_INCREF(ret);
87         return ret;
88     }
89     if (PyUnicode_CheckExact(key)) {
90         Py_INCREF(key);
91         return key;
92     }
93     if (PyUnicode_Check(key)) {
94         return PyObject_Str(key);
95     }
96     PyErr_SetString(PyExc_TypeError,
97                     "MultiDict keys should be either str "
98                     "or subclasses of str");
99     return NULL;
100 }
101 
102 
103 static inline PyObject *
ci_key_to_str(PyObject * key)104 ci_key_to_str(PyObject *key)
105 {
106     PyObject *ret;
107     PyTypeObject *type = Py_TYPE(key);
108     if (type == &istr_type) {
109         ret = ((istrobject*)key)->canonical;
110         Py_INCREF(ret);
111         return ret;
112     }
113     if (PyUnicode_Check(key)) {
114         return _PyObject_CallMethodId(key, &PyId_lower, NULL);
115     }
116     PyErr_SetString(PyExc_TypeError,
117                     "CIMultiDict keys should be either str "
118                     "or subclasses of str");
119     return NULL;
120 }
121 
122 static inline pair_t *
pair_list_get(pair_list_t * list,Py_ssize_t i)123 pair_list_get(pair_list_t *list, Py_ssize_t i)
124 {
125     pair_t *item = list->pairs + i;
126     return item;
127 }
128 
129 
130 static inline int
pair_list_grow(pair_list_t * list)131 pair_list_grow(pair_list_t *list)
132 {
133     // Grow by one element if needed
134     Py_ssize_t new_capacity;
135     pair_t *new_pairs;
136 
137     if (list->size < list->capacity) {
138         return 0;
139     }
140 
141     if (list->pairs == list->buffer) {
142         new_pairs = PyMem_New(pair_t, MIN_CAPACITY);
143         memcpy(new_pairs, list->buffer, (size_t)list->capacity * sizeof(pair_t));
144 
145         list->pairs = new_pairs;
146         list->capacity = MIN_CAPACITY;
147         return 0;
148     } else {
149         new_capacity = list->capacity + CAPACITY_STEP;
150         new_pairs = PyMem_Resize(list->pairs, pair_t, (size_t)new_capacity);
151 
152         if (NULL == new_pairs) {
153             // Resizing error
154             return -1;
155         }
156 
157         list->pairs = new_pairs;
158         list->capacity = new_capacity;
159         return 0;
160     }
161 }
162 
163 
164 static inline int
pair_list_shrink(pair_list_t * list)165 pair_list_shrink(pair_list_t *list)
166 {
167     // Shrink by one element if needed.
168     // Optimization is applied to prevent jitter
169     // (grow-shrink-grow-shrink on adding-removing the single element
170     // when the buffer is full).
171     // To prevent this, the buffer is resized if the size is less than the capacity
172     // by 2*CAPACITY_STEP factor.
173     // The switch back to embedded buffer is never performed for both reasons:
174     // the code simplicity and the jitter prevention.
175 
176     pair_t *new_pairs;
177     Py_ssize_t new_capacity;
178 
179     if (list->capacity - list->size < 2 * CAPACITY_STEP) {
180         return 0;
181     }
182     new_capacity = list->capacity - CAPACITY_STEP;
183     if (new_capacity < MIN_CAPACITY) {
184         return 0;
185     }
186 
187     new_pairs = PyMem_Resize(list->pairs, pair_t, (size_t)new_capacity);
188 
189     if (NULL == new_pairs) {
190         // Resizing error
191         return -1;
192     }
193 
194     list->pairs = new_pairs;
195     list->capacity = new_capacity;
196 
197     return 0;
198 }
199 
200 
201 static inline int
_pair_list_init(pair_list_t * list,calc_identity_func calc_identity)202 _pair_list_init(pair_list_t *list, calc_identity_func calc_identity)
203 {
204     list->pairs = list->buffer;
205     list->capacity = EMBEDDED_CAPACITY;
206     list->size = 0;
207     list->version = NEXT_VERSION();
208     list->calc_identity = calc_identity;
209     return 0;
210 }
211 
212 static inline int
pair_list_init(pair_list_t * list)213 pair_list_init(pair_list_t *list)
214 {
215     return _pair_list_init(list, key_to_str);
216 }
217 
218 
219 static inline int
ci_pair_list_init(pair_list_t * list)220 ci_pair_list_init(pair_list_t *list)
221 {
222     return _pair_list_init(list, ci_key_to_str);
223 }
224 
225 
226 static inline void
pair_list_dealloc(pair_list_t * list)227 pair_list_dealloc(pair_list_t *list)
228 {
229     pair_t *pair;
230     Py_ssize_t pos;
231 
232     for (pos = 0; pos < list->size; pos++) {
233         pair = pair_list_get(list, pos);
234 
235         Py_XDECREF(pair->identity);
236         Py_XDECREF(pair->key);
237         Py_XDECREF(pair->value);
238     }
239 
240     /*
241     Strictly speaking, resetting size and capacity and
242     assigning pairs to buffer is not necessary.
243     Do it to consistency and idemotency.
244     The cleanup doesn't hurt performance.
245     !!!
246     !!! The buffer deletion is crucial though.
247     !!!
248     */
249     list->size = 0;
250     if (list->pairs != list->buffer) {
251         PyMem_Del(list->pairs);
252         list->pairs = list->buffer;
253         list->capacity = EMBEDDED_CAPACITY;
254     }
255 }
256 
257 
258 static inline Py_ssize_t
pair_list_len(pair_list_t * list)259 pair_list_len(pair_list_t *list)
260 {
261     return list->size;
262 }
263 
264 
265 static inline int
_pair_list_add_with_hash(pair_list_t * list,PyObject * identity,PyObject * key,PyObject * value,Py_hash_t hash)266 _pair_list_add_with_hash(pair_list_t *list,
267                          PyObject *identity,
268                          PyObject *key,
269                          PyObject *value,
270                          Py_hash_t hash)
271 {
272     pair_t *pair;
273 
274     if (pair_list_grow(list) < 0) {
275         return -1;
276     }
277 
278     pair = pair_list_get(list, list->size);
279 
280     Py_INCREF(identity);
281     pair->identity = identity;
282 
283     Py_INCREF(key);
284     pair->key = key;
285 
286     Py_INCREF(value);
287     pair->value = value;
288 
289     pair->hash = hash;
290 
291     list->version = NEXT_VERSION();
292     list->size += 1;
293 
294     return 0;
295 }
296 
297 
298 static inline int
pair_list_add(pair_list_t * list,PyObject * key,PyObject * value)299 pair_list_add(pair_list_t *list,
300               PyObject *key,
301               PyObject *value)
302 {
303     Py_hash_t hash;
304     PyObject *identity = NULL;
305     int ret;
306 
307     identity = list->calc_identity(key);
308     if (identity == NULL) {
309         goto fail;
310     }
311     hash = PyObject_Hash(identity);
312     if (hash == -1) {
313         goto fail;
314     }
315     ret = _pair_list_add_with_hash(list, identity, key, value, hash);
316     Py_DECREF(identity);
317     return ret;
318 fail:
319     Py_XDECREF(identity);
320     return -1;
321 }
322 
323 
324 static inline int
pair_list_del_at(pair_list_t * list,Py_ssize_t pos)325 pair_list_del_at(pair_list_t *list, Py_ssize_t pos)
326 {
327     // return 1 on success, -1 on failure
328     Py_ssize_t tail;
329     pair_t *pair;
330 
331     pair = pair_list_get(list, pos);
332     Py_DECREF(pair->identity);
333     Py_DECREF(pair->key);
334     Py_DECREF(pair->value);
335 
336     list->size -= 1;
337     list->version = NEXT_VERSION();
338 
339     if (list->size == pos) {
340         // remove from tail, no need to shift body
341         return 0;
342     }
343 
344     tail = list->size - pos;
345     // TODO: raise an error if tail < 0
346     memmove((void *)pair_list_get(list, pos),
347             (void *)pair_list_get(list, pos + 1),
348             sizeof(pair_t) * (size_t)tail);
349 
350     return pair_list_shrink(list);
351 }
352 
353 
354 static inline int
_pair_list_drop_tail(pair_list_t * list,PyObject * identity,Py_hash_t hash,Py_ssize_t pos)355 _pair_list_drop_tail(pair_list_t *list, PyObject *identity, Py_hash_t hash,
356                      Py_ssize_t pos)
357 {
358     // return 1 if deleted, 0 if not found
359     pair_t *pair;
360     int ret;
361     int found = 0;
362 
363     if (pos >= list->size) {
364         return 0;
365     }
366 
367     for (; pos < list->size; pos++) {
368         pair = pair_list_get(list, pos);
369         if (pair->hash != hash) {
370             continue;
371         }
372         ret = str_cmp(pair->identity, identity);
373         if (ret > 0) {
374             if (pair_list_del_at(list, pos) < 0) {
375                return -1;
376             }
377             found = 1;
378             pos--;
379         }
380         else if (ret == -1) {
381             return -1;
382         }
383     }
384 
385     return found;
386 }
387 
388 static inline int
_pair_list_del_hash(pair_list_t * list,PyObject * identity,PyObject * key,Py_hash_t hash)389 _pair_list_del_hash(pair_list_t *list, PyObject *identity,
390                     PyObject *key, Py_hash_t hash)
391 {
392     int ret = _pair_list_drop_tail(list, identity, hash, 0);
393 
394     if (ret < 0) {
395         return -1;
396     }
397     else if (ret == 0) {
398         PyErr_SetObject(PyExc_KeyError, key);
399         return -1;
400     }
401     else {
402         list->version = NEXT_VERSION();
403         return 0;
404     }
405 }
406 
407 
408 static inline int
pair_list_del(pair_list_t * list,PyObject * key)409 pair_list_del(pair_list_t *list, PyObject *key)
410 {
411     PyObject *identity = NULL;
412     Py_hash_t hash;
413     int ret;
414 
415     identity = list->calc_identity(key);
416     if (identity == NULL) {
417         goto fail;
418     }
419 
420     hash = PyObject_Hash(identity);
421     if (hash == -1) {
422         goto fail;
423     }
424 
425     ret = _pair_list_del_hash(list, identity, key, hash);
426     Py_DECREF(identity);
427     return ret;
428 fail:
429     Py_XDECREF(identity);
430     return -1;
431 }
432 
433 
434 static inline uint64_t
pair_list_version(pair_list_t * list)435 pair_list_version(pair_list_t *list)
436 {
437     return list->version;
438 }
439 
440 
441 static inline int
_pair_list_next(pair_list_t * list,Py_ssize_t * ppos,PyObject ** pidentity,PyObject ** pkey,PyObject ** pvalue,Py_hash_t * phash)442 _pair_list_next(pair_list_t *list, Py_ssize_t *ppos, PyObject **pidentity,
443                 PyObject **pkey, PyObject **pvalue, Py_hash_t *phash)
444 {
445     pair_t *pair;
446 
447     if (*ppos >= list->size) {
448         return 0;
449     }
450 
451     pair = pair_list_get(list, *ppos);
452 
453     if (pidentity) {
454         *pidentity = pair->identity;
455     }
456     if (pkey) {
457         *pkey = pair->key;
458     }
459     if (pvalue) {
460         *pvalue = pair->value;
461     }
462     if (phash) {
463         *phash = pair->hash;
464     }
465 
466     *ppos += 1;
467     return 1;
468 }
469 
470 
471 static inline int
pair_list_next(pair_list_t * list,Py_ssize_t * ppos,PyObject ** pidentity,PyObject ** pkey,PyObject ** pvalue)472 pair_list_next(pair_list_t *list, Py_ssize_t *ppos, PyObject **pidentity,
473                PyObject **pkey, PyObject **pvalue)
474 {
475     Py_hash_t hash;
476     return _pair_list_next(list, ppos, pidentity, pkey, pvalue, &hash);
477 }
478 
479 
480 static inline int
pair_list_contains(pair_list_t * list,PyObject * key)481 pair_list_contains(pair_list_t *list, PyObject *key)
482 {
483     Py_hash_t hash1, hash2;
484     Py_ssize_t pos = 0;
485     PyObject *ident = NULL;
486     PyObject *identity = NULL;
487     int tmp;
488 
489     ident = list->calc_identity(key);
490     if (ident == NULL) {
491         goto fail;
492     }
493 
494     hash1 = PyObject_Hash(ident);
495     if (hash1 == -1) {
496         goto fail;
497     }
498 
499     while (_pair_list_next(list, &pos, &identity, NULL, NULL, &hash2)) {
500         if (hash1 != hash2) {
501             continue;
502         }
503         tmp = str_cmp(ident, identity);
504         if (tmp > 0) {
505             Py_DECREF(ident);
506             return 1;
507         }
508         else if (tmp < 0) {
509             goto fail;
510         }
511     }
512 
513     Py_DECREF(ident);
514     return 0;
515 fail:
516     Py_XDECREF(ident);
517     return -1;
518 }
519 
520 
521 static inline PyObject *
pair_list_get_one(pair_list_t * list,PyObject * key)522 pair_list_get_one(pair_list_t *list, PyObject *key)
523 {
524     Py_hash_t hash1, hash2;
525     Py_ssize_t pos = 0;
526     PyObject *ident = NULL;
527     PyObject *identity = NULL;
528     PyObject *value = NULL;
529     int tmp;
530 
531     ident = list->calc_identity(key);
532     if (ident == NULL) {
533         goto fail;
534     }
535 
536     hash1 = PyObject_Hash(ident);
537     if (hash1 == -1) {
538         goto fail;
539     }
540 
541     while (_pair_list_next(list, &pos, &identity, NULL, &value, &hash2)) {
542         if (hash1 != hash2) {
543             continue;
544         }
545         tmp = str_cmp(ident, identity);
546         if (tmp > 0) {
547             Py_INCREF(value);
548             Py_DECREF(ident);
549             return value;
550         }
551         else if (tmp < 0) {
552             goto fail;
553         }
554     }
555 
556     Py_DECREF(ident);
557     PyErr_SetObject(PyExc_KeyError, key);
558     return NULL;
559 fail:
560     Py_XDECREF(ident);
561     return NULL;
562 }
563 
564 
565 static inline PyObject *
pair_list_get_all(pair_list_t * list,PyObject * key)566 pair_list_get_all(pair_list_t *list, PyObject *key)
567 {
568     Py_hash_t hash1, hash2;
569     Py_ssize_t pos = 0;
570     PyObject *ident = NULL;
571     PyObject *identity = NULL;
572     PyObject *value = NULL;
573     PyObject *res = NULL;
574     int tmp;
575 
576     ident = list->calc_identity(key);
577     if (ident == NULL) {
578         goto fail;
579     }
580 
581     hash1 = PyObject_Hash(ident);
582     if (hash1 == -1) {
583         goto fail;
584     }
585 
586     while (_pair_list_next(list, &pos, &identity, NULL, &value, &hash2)) {
587         if (hash1 != hash2) {
588             continue;
589         }
590         tmp = str_cmp(ident, identity);
591         if (tmp > 0) {
592             if (res == NULL) {
593                 res = PyList_New(1);
594                 if (res == NULL) {
595                     goto fail;
596                 }
597                 if (PyList_SetItem(res, 0, value) < 0) {
598                     goto fail;
599                 }
600                 Py_INCREF(value);
601             }
602             else if (PyList_Append(res, value) < 0) {
603                 goto fail;
604             }
605         }
606         else if (tmp < 0) {
607             goto fail;
608         }
609     }
610 
611     if (res == NULL) {
612         PyErr_SetObject(PyExc_KeyError, key);
613     }
614     Py_DECREF(ident);
615     return res;
616 
617 fail:
618     Py_XDECREF(ident);
619     Py_XDECREF(res);
620     return NULL;
621 }
622 
623 
624 static inline PyObject *
pair_list_set_default(pair_list_t * list,PyObject * key,PyObject * value)625 pair_list_set_default(pair_list_t *list, PyObject *key, PyObject *value)
626 {
627     Py_hash_t hash1, hash2;
628     Py_ssize_t pos = 0;
629     PyObject *ident = NULL;
630     PyObject *identity = NULL;
631     PyObject *value2 = NULL;
632     int tmp;
633 
634     ident = list->calc_identity(key);
635     if (ident == NULL) {
636         goto fail;
637     }
638 
639     hash1 = PyObject_Hash(ident);
640     if (hash1 == -1) {
641         goto fail;
642     }
643 
644     while (_pair_list_next(list, &pos, &identity, NULL, &value2, &hash2)) {
645         if (hash1 != hash2) {
646             continue;
647         }
648         tmp = str_cmp(ident, identity);
649         if (tmp > 0) {
650             Py_INCREF(value2);
651             Py_DECREF(ident);
652             return value2;
653         }
654         else if (tmp < 0) {
655             goto fail;
656         }
657     }
658 
659     if (_pair_list_add_with_hash(list, ident, key, value, hash1) < 0) {
660         goto fail;
661     }
662 
663     Py_INCREF(value);
664     Py_DECREF(ident);
665     return value;
666 fail:
667     Py_XDECREF(ident);
668     return NULL;
669 }
670 
671 
672 static inline PyObject *
pair_list_pop_one(pair_list_t * list,PyObject * key)673 pair_list_pop_one(pair_list_t *list, PyObject *key)
674 {
675     pair_t *pair;
676 
677     Py_hash_t hash;
678     Py_ssize_t pos;
679     PyObject *value = NULL;
680     int tmp;
681     PyObject *ident = NULL;
682 
683     ident = list->calc_identity(key);
684     if (ident == NULL) {
685         goto fail;
686     }
687 
688     hash = PyObject_Hash(ident);
689     if (hash == -1) {
690         goto fail;
691     }
692 
693     for (pos=0; pos < list->size; pos++) {
694         pair = pair_list_get(list, pos);
695         if (pair->hash != hash) {
696             continue;
697         }
698         tmp = str_cmp(ident, pair->identity);
699         if (tmp > 0) {
700             value = pair->value;
701             Py_INCREF(value);
702             if (pair_list_del_at(list, pos) < 0) {
703                 goto fail;
704             }
705             Py_DECREF(ident);
706             return value;
707         }
708         else if (tmp < 0) {
709             goto fail;
710         }
711     }
712 
713     PyErr_SetObject(PyExc_KeyError, key);
714     goto fail;
715 
716 fail:
717     Py_XDECREF(value);
718     Py_XDECREF(ident);
719     return NULL;
720 }
721 
722 
723 static inline PyObject *
pair_list_pop_all(pair_list_t * list,PyObject * key)724 pair_list_pop_all(pair_list_t *list, PyObject *key)
725 {
726     Py_hash_t hash;
727     Py_ssize_t pos;
728     pair_t *pair;
729     int tmp;
730     PyObject *res = NULL;
731     PyObject *ident = NULL;
732 
733     ident = list->calc_identity(key);
734     if (ident == NULL) {
735         goto fail;
736     }
737 
738     hash = PyObject_Hash(ident);
739     if (hash == -1) {
740         goto fail;
741     }
742 
743     if (list->size == 0) {
744         PyErr_SetObject(PyExc_KeyError, ident);
745         goto fail;
746     }
747 
748     for (pos = list->size - 1; pos >= 0; pos--) {
749         pair = pair_list_get(list, pos);
750         if (hash != pair->hash) {
751             continue;
752         }
753         tmp = str_cmp(ident, pair->identity);
754         if (tmp > 0) {
755             if (res == NULL) {
756                 res = PyList_New(1);
757                 if (res == NULL) {
758                     goto fail;
759                 }
760                 if (PyList_SetItem(res, 0, pair->value) < 0) {
761                     goto fail;
762                 }
763                 Py_INCREF(pair->value);
764             } else if (PyList_Append(res, pair->value) < 0) {
765                 goto fail;
766             }
767             if (pair_list_del_at(list, pos) < 0) {
768                 goto fail;
769             }
770         }
771         else if (tmp < 0) {
772             goto fail;
773         }
774     }
775 
776     if (res == NULL) {
777         PyErr_SetObject(PyExc_KeyError, key);
778     } else if (PyList_Reverse(res) < 0) {
779         goto fail;
780     }
781     Py_DECREF(ident);
782     return res;
783 
784 fail:
785     Py_XDECREF(ident);
786     Py_XDECREF(res);
787     return NULL;
788 }
789 
790 
791 static inline PyObject *
pair_list_pop_item(pair_list_t * list)792 pair_list_pop_item(pair_list_t *list)
793 {
794     PyObject *ret;
795     pair_t *pair;
796 
797     if (list->size == 0) {
798         PyErr_SetString(PyExc_KeyError, "empty multidict");
799         return NULL;
800     }
801 
802     pair = pair_list_get(list, 0);
803     ret = PyTuple_Pack(2, pair->key, pair->value);
804     if (ret == NULL) {
805         return NULL;
806     }
807 
808     if (pair_list_del_at(list, 0) < 0) {
809         Py_DECREF(ret);
810         return NULL;
811     }
812 
813     return ret;
814 }
815 
816 
817 static inline int
pair_list_replace(pair_list_t * list,PyObject * key,PyObject * value)818 pair_list_replace(pair_list_t *list, PyObject * key, PyObject *value)
819 {
820     pair_t *pair;
821 
822     Py_ssize_t pos;
823     int tmp;
824     int found = 0;
825 
826     PyObject *identity = NULL;
827     Py_hash_t hash;
828 
829     identity = list->calc_identity(key);
830     if (identity == NULL) {
831         goto fail;
832     }
833 
834     hash = PyObject_Hash(identity);
835     if (hash == -1) {
836         goto fail;
837     }
838 
839 
840     for (pos = 0; pos < list->size; pos++) {
841         pair = pair_list_get(list, pos);
842         if (hash != pair->hash) {
843             continue;
844         }
845         tmp = str_cmp(identity, pair->identity);
846         if (tmp > 0) {
847             found = 1;
848             Py_INCREF(key);
849             Py_DECREF(pair->key);
850             pair->key = key;
851             Py_INCREF(value);
852             Py_DECREF(pair->value);
853             pair->value = value;
854             break;
855         }
856         else if (tmp < 0) {
857             goto fail;
858         }
859     }
860 
861     if (!found) {
862         if (_pair_list_add_with_hash(list, identity, key, value, hash) < 0) {
863             goto fail;
864         }
865         Py_DECREF(identity);
866         return 0;
867     }
868     else {
869         list->version = NEXT_VERSION();
870         if (_pair_list_drop_tail(list, identity, hash, pos+1) < 0) {
871             goto fail;
872         }
873         Py_DECREF(identity);
874         return 0;
875     }
876 fail:
877     Py_XDECREF(identity);
878     return -1;
879 }
880 
881 
882 static inline int
_dict_set_number(PyObject * dict,PyObject * key,Py_ssize_t num)883 _dict_set_number(PyObject *dict, PyObject *key, Py_ssize_t num)
884 {
885     PyObject *tmp = PyLong_FromSsize_t(num);
886     if (tmp == NULL) {
887        return -1;
888     }
889 
890     if (PyDict_SetItem(dict, key, tmp) < 0) {
891         Py_DECREF(tmp);
892         return -1;
893     }
894 
895     return 0;
896 }
897 
898 
899 static inline int
_pair_list_post_update(pair_list_t * list,PyObject * used_keys,Py_ssize_t pos)900 _pair_list_post_update(pair_list_t *list, PyObject* used_keys, Py_ssize_t pos)
901 {
902     pair_t *pair;
903     PyObject *tmp;
904     Py_ssize_t num;
905 
906     for (; pos < list->size; pos++) {
907         pair = pair_list_get(list, pos);
908         tmp = PyDict_GetItem(used_keys, pair->identity);
909         if (tmp == NULL) {
910             // not found
911             continue;
912         }
913 
914         num = PyLong_AsSsize_t(tmp);
915         if (num == -1) {
916             if (!PyErr_Occurred()) {
917                 PyErr_SetString(PyExc_RuntimeError, "invalid internal state");
918             }
919             return -1;
920         }
921 
922         if (pos >= num) {
923             // del self[pos]
924             if (pair_list_del_at(list, pos) < 0) {
925                 return -1;
926             }
927             pos--;
928         }
929     }
930 
931     list->version = NEXT_VERSION();
932     return 0;
933 }
934 
935 // TODO: need refactoring function name
936 static inline int
_pair_list_update(pair_list_t * list,PyObject * key,PyObject * value,PyObject * used_keys,PyObject * identity,Py_hash_t hash)937 _pair_list_update(pair_list_t *list, PyObject *key,
938                   PyObject *value, PyObject *used_keys,
939                   PyObject *identity, Py_hash_t hash)
940 {
941     PyObject *item = NULL;
942     pair_t *pair = NULL;
943     Py_ssize_t pos;
944     int found;
945     int ident_cmp_res;
946 
947     item = PyDict_GetItem(used_keys, identity);
948     if (item == NULL) {
949         pos = 0;
950     }
951     else {
952         pos = PyLong_AsSsize_t(item);
953         if (pos == -1) {
954             if (!PyErr_Occurred()) {
955                 PyErr_SetString(PyExc_RuntimeError, "invalid internal state");
956             }
957             return -1;
958         }
959     }
960 
961     found = 0;
962     for (; pos < list->size; pos++) {
963         pair = pair_list_get(list, pos);
964         if (pair->hash != hash) {
965             continue;
966         }
967 
968         ident_cmp_res = str_cmp(pair->identity, identity);
969         if (ident_cmp_res > 0) {
970             Py_INCREF(key);
971             Py_DECREF(pair->key);
972             pair->key = key;
973 
974             Py_INCREF(value);
975             Py_DECREF(pair->value);
976             pair->value = value;
977 
978             if (_dict_set_number(used_keys, pair->identity, pos + 1) < 0) {
979                 return -1;
980             }
981 
982             found = 1;
983             break;
984         }
985         else if (ident_cmp_res < 0) {
986             return -1;
987         }
988     }
989 
990     if (!found) {
991         if (_pair_list_add_with_hash(list, identity, key, value, hash) < 0) {
992             return -1;
993         }
994         if (_dict_set_number(used_keys, identity, list->size) < 0) {
995             return -1;
996         }
997     }
998 
999     return 0;
1000 }
1001 
1002 
1003 static inline int
pair_list_update(pair_list_t * list,pair_list_t * other)1004 pair_list_update(pair_list_t *list, pair_list_t *other)
1005 {
1006     PyObject *used_keys = NULL;
1007     pair_t *pair = NULL;
1008 
1009     Py_ssize_t pos;
1010 
1011     if (other->size == 0) {
1012         return 0;
1013     }
1014 
1015     used_keys = PyDict_New();
1016     if (used_keys == NULL) {
1017         return -1;
1018     }
1019 
1020     for (pos = 0; pos < other->size; pos++) {
1021         pair = pair_list_get(other, pos);
1022         if (_pair_list_update(list, pair->key, pair->value, used_keys,
1023                               pair->identity, pair->hash) < 0) {
1024             goto fail;
1025         }
1026     }
1027 
1028     if (_pair_list_post_update(list, used_keys, 0) < 0) {
1029         goto fail;
1030     }
1031 
1032     Py_DECREF(used_keys);
1033     return 0;
1034 
1035 fail:
1036     Py_XDECREF(used_keys);
1037     return -1;
1038 }
1039 
1040 
1041 static inline int
pair_list_update_from_seq(pair_list_t * list,PyObject * seq)1042 pair_list_update_from_seq(pair_list_t *list, PyObject *seq)
1043 {
1044     PyObject *it = NULL; // iter(seq)
1045     PyObject *fast = NULL; // item as a 2-tuple or 2-list
1046     PyObject *item = NULL; // seq[i]
1047     PyObject *used_keys = NULL; // dict(<Identitty: Pos>)
1048 
1049     PyObject *key = NULL;
1050     PyObject *value = NULL;
1051     PyObject *identity = NULL;
1052 
1053     Py_hash_t hash;
1054 
1055     Py_ssize_t i;
1056     Py_ssize_t n;
1057 
1058     it = PyObject_GetIter(seq);
1059     if (it == NULL) {
1060         return -1;
1061     }
1062 
1063     used_keys = PyDict_New();
1064     if (used_keys == NULL) {
1065         goto fail_1;
1066     }
1067 
1068     for (i = 0; ; ++i) { // i - index into seq of current element
1069         fast = NULL;
1070         item = PyIter_Next(it);
1071         if (item == NULL) {
1072             if (PyErr_Occurred()) {
1073                 goto fail_1;
1074             }
1075             break;
1076         }
1077 
1078         // Convert item to sequence, and verify length 2.
1079         fast = PySequence_Fast(item, "");
1080         if (fast == NULL) {
1081             if (PyErr_ExceptionMatches(PyExc_TypeError)) {
1082                 PyErr_Format(PyExc_TypeError,
1083                              "multidict cannot convert sequence element #%zd"
1084                              " to a sequence",
1085                              i);
1086             }
1087             goto fail_1;
1088         }
1089 
1090         n = PySequence_Fast_GET_SIZE(fast);
1091         if (n != 2) {
1092             PyErr_Format(PyExc_ValueError,
1093                          "multidict update sequence element #%zd "
1094                          "has length %zd; 2 is required",
1095                          i, n);
1096             goto fail_1;
1097         }
1098 
1099         key = PySequence_Fast_GET_ITEM(fast, 0);
1100         value = PySequence_Fast_GET_ITEM(fast, 1);
1101         Py_INCREF(key);
1102         Py_INCREF(value);
1103 
1104         identity = list->calc_identity(key);
1105         if (identity == NULL) {
1106             goto fail_1;
1107         }
1108 
1109         hash = PyObject_Hash(identity);
1110         if (hash == -1) {
1111             goto fail_1;
1112         }
1113 
1114         if (_pair_list_update(list, key, value, used_keys, identity, hash) < 0) {
1115             goto fail_1;
1116         }
1117 
1118         Py_DECREF(key);
1119         Py_DECREF(value);
1120         Py_DECREF(fast);
1121         Py_DECREF(item);
1122         Py_DECREF(identity);
1123     }
1124 
1125     if (_pair_list_post_update(list, used_keys, 0) < 0) {
1126         goto fail_2;
1127     }
1128 
1129     Py_DECREF(it);
1130     Py_DECREF(used_keys);
1131     return 0;
1132 
1133 fail_1:
1134     Py_XDECREF(key);
1135     Py_XDECREF(value);
1136     Py_XDECREF(fast);
1137     Py_XDECREF(item);
1138     Py_XDECREF(identity);
1139 
1140 fail_2:
1141     Py_XDECREF(it);
1142     Py_XDECREF(used_keys);
1143     return -1;
1144 }
1145 
1146 static inline int
pair_list_eq_to_mapping(pair_list_t * list,PyObject * other)1147 pair_list_eq_to_mapping(pair_list_t *list, PyObject *other)
1148 {
1149     PyObject *key = NULL;
1150     PyObject *avalue = NULL;
1151     PyObject *bvalue;
1152 
1153     Py_ssize_t pos, other_len;
1154 
1155     int eq;
1156 
1157     if (!PyMapping_Check(other)) {
1158         PyErr_Format(PyExc_TypeError,
1159                      "other argument must be a mapping, not %s",
1160                      Py_TYPE(other)->tp_name);
1161         return -1;
1162     }
1163 
1164     other_len = PyMapping_Size(other);
1165     if (other_len < 0) {
1166         return -1;
1167     }
1168     if (pair_list_len(list) != other_len) {
1169         return 0;
1170     }
1171 
1172     pos = 0;
1173     while (pair_list_next(list, &pos, NULL, &key, &avalue)) {
1174         bvalue = PyObject_GetItem(other, key);
1175         if (bvalue == NULL) {
1176             if (PyErr_ExceptionMatches(PyExc_KeyError)) {
1177                 PyErr_Clear();
1178                 return 0;
1179             }
1180             return -1;
1181         }
1182 
1183         eq = PyObject_RichCompareBool(avalue, bvalue, Py_EQ);
1184         Py_DECREF(bvalue);
1185 
1186         if (eq <= 0) {
1187             return eq;
1188         }
1189     }
1190 
1191     return 1;
1192 }
1193 
1194 
1195 /***********************************************************************/
1196 
1197 static inline int
pair_list_traverse(pair_list_t * list,visitproc visit,void * arg)1198 pair_list_traverse(pair_list_t *list, visitproc visit, void *arg)
1199 {
1200     pair_t *pair = NULL;
1201     Py_ssize_t pos;
1202 
1203     for (pos = 0; pos < list->size; pos++) {
1204         pair = pair_list_get(list, pos);
1205         // Don't need traverse the identity: it is a terminal
1206         Py_VISIT(pair->key);
1207         Py_VISIT(pair->value);
1208     }
1209 
1210     return 0;
1211 }
1212 
1213 
1214 static inline int
pair_list_clear(pair_list_t * list)1215 pair_list_clear(pair_list_t *list)
1216 {
1217     pair_t *pair = NULL;
1218     Py_ssize_t pos;
1219 
1220     if (list->size == 0) {
1221         return 0;
1222     }
1223 
1224     list->version = NEXT_VERSION();
1225     for (pos = 0; pos < list->size; pos++) {
1226         pair = pair_list_get(list, pos);
1227         Py_CLEAR(pair->key);
1228         Py_CLEAR(pair->identity);
1229         Py_CLEAR(pair->value);
1230     }
1231     list->size = 0;
1232     if (list->pairs != list->buffer) {
1233         PyMem_Del(list->pairs);
1234         list->pairs = list->buffer;
1235     }
1236 
1237     return 0;
1238 }
1239 
1240 
1241 #ifdef __cplusplus
1242 }
1243 #endif
1244 #endif
1245