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