blob: b3cbf9ac820879ec894c6b9e8d6f40914a189aa3 [file] [log] [blame]
Yury Selivanovf23746a2018-01-22 19:11:18 -05001#include "Python.h"
2
Victor Stinner27e2d1f2018-11-01 00:52:28 +01003#include "pycore_hamt.h"
Victor Stinnerbcda8f12018-11-21 22:27:47 +01004#include "pycore_object.h"
5#include "pycore_pystate.h"
6#include "structmember.h"
Yury Selivanovf23746a2018-01-22 19:11:18 -05007
Yury Selivanovf23746a2018-01-22 19:11:18 -05008/*
9This file provides an implemention of an immutable mapping using the
10Hash Array Mapped Trie (or HAMT) datastructure.
11
12This design allows to have:
13
141. Efficient copy: immutable mappings can be copied by reference,
15 making it an O(1) operation.
16
172. Efficient mutations: due to structural sharing, only a portion of
18 the trie needs to be copied when the collection is mutated. The
19 cost of set/delete operations is O(log N).
20
213. Efficient lookups: O(log N).
22
23(where N is number of key/value items in the immutable mapping.)
24
25
26HAMT
27====
28
29The core idea of HAMT is that the shape of the trie is encoded into the
30hashes of keys.
31
32Say we want to store a K/V pair in our mapping. First, we calculate the
33hash of K, let's say it's 19830128, or in binary:
34
35 0b1001011101001010101110000 = 19830128
36
37Now let's partition this bit representation of the hash into blocks of
385 bits each:
39
40 0b00_00000_10010_11101_00101_01011_10000 = 19830128
41 (6) (5) (4) (3) (2) (1)
42
Ville Skyttä61f82e02018-04-20 23:08:45 +030043Each block of 5 bits represents a number between 0 and 31. So if we have
Yury Selivanovf23746a2018-01-22 19:11:18 -050044a tree that consists of nodes, each of which is an array of 32 pointers,
45those 5-bit blocks will encode a position on a single tree level.
46
47For example, storing the key K with hash 19830128, results in the following
48tree structure:
49
50 (array of 32 pointers)
51 +---+ -- +----+----+----+ -- +----+
52 root node | 0 | .. | 15 | 16 | 17 | .. | 31 | 0b10000 = 16 (1)
53 (level 1) +---+ -- +----+----+----+ -- +----+
54 |
55 +---+ -- +----+----+----+ -- +----+
56 a 2nd level node | 0 | .. | 10 | 11 | 12 | .. | 31 | 0b01011 = 11 (2)
57 +---+ -- +----+----+----+ -- +----+
58 |
59 +---+ -- +----+----+----+ -- +----+
Dmitry Alimov93a61192018-01-25 20:54:41 +030060 a 3rd level node | 0 | .. | 04 | 05 | 06 | .. | 31 | 0b00101 = 5 (3)
Yury Selivanovf23746a2018-01-22 19:11:18 -050061 +---+ -- +----+----+----+ -- +----+
62 |
63 +---+ -- +----+----+----+----+
64 a 4th level node | 0 | .. | 04 | 29 | 30 | 31 | 0b11101 = 29 (4)
65 +---+ -- +----+----+----+----+
66 |
67 +---+ -- +----+----+----+ -- +----+
68 a 5th level node | 0 | .. | 17 | 18 | 19 | .. | 31 | 0b10010 = 18 (5)
69 +---+ -- +----+----+----+ -- +----+
70 |
71 +--------------+
72 |
73 +---+ -- +----+----+----+ -- +----+
74 a 6th level node | 0 | .. | 15 | 16 | 17 | .. | 31 | 0b00000 = 0 (6)
75 +---+ -- +----+----+----+ -- +----+
76 |
77 V -- our value (or collision)
78
79To rehash: for a K/V pair, the hash of K encodes where in the tree V will
80be stored.
81
82To optimize memory footprint and handle hash collisions, our implementation
83uses three different types of nodes:
84
85 * A Bitmap node;
86 * An Array node;
87 * A Collision node.
88
89Because we implement an immutable dictionary, our nodes are also
90immutable. Therefore, when we need to modify a node, we copy it, and
91do that modification to the copy.
92
93
94Array Nodes
95-----------
96
97These nodes are very simple. Essentially they are arrays of 32 pointers
98we used to illustrate the high-level idea in the previous section.
99
100We use Array nodes only when we need to store more than 16 pointers
101in a single node.
102
103Array nodes do not store key objects or value objects. They are used
104only as an indirection level - their pointers point to other nodes in
105the tree.
106
107
108Bitmap Node
109-----------
110
111Allocating a new 32-pointers array for every node of our tree would be
112very expensive. Unless we store millions of keys, most of tree nodes would
113be very sparse.
114
115When we have less than 16 elements in a node, we don't want to use the
116Array node, that would mean that we waste a lot of memory. Instead,
117we can use bitmap compression and can have just as many pointers
118as we need!
119
120Bitmap nodes consist of two fields:
121
1221. An array of pointers. If a Bitmap node holds N elements, the
123 array will be of N pointers.
124
1252. A 32bit integer -- a bitmap field. If an N-th bit is set in the
126 bitmap, it means that the node has an N-th element.
127
128For example, say we need to store a 3 elements sparse array:
129
130 +---+ -- +---+ -- +----+ -- +----+
131 | 0 | .. | 4 | .. | 11 | .. | 17 |
132 +---+ -- +---+ -- +----+ -- +----+
133 | | |
134 o1 o2 o3
135
136We allocate a three-pointer Bitmap node. Its bitmap field will be
137then set to:
138
139 0b_00100_00010_00000_10000 == (1 << 17) | (1 << 11) | (1 << 4)
140
141To check if our Bitmap node has an I-th element we can do:
142
143 bitmap & (1 << I)
144
145
146And here's a formula to calculate a position in our pointer array
147which would correspond to an I-th element:
148
149 popcount(bitmap & ((1 << I) - 1))
150
151
152Let's break it down:
153
154 * `popcount` is a function that returns a number of bits set to 1;
155
156 * `((1 << I) - 1)` is a mask to filter the bitmask to contain bits
157 set to the *right* of our bit.
158
159
160So for our 17, 11, and 4 indexes:
161
162 * bitmap & ((1 << 17) - 1) == 0b100000010000 => 2 bits are set => index is 2.
163
164 * bitmap & ((1 << 11) - 1) == 0b10000 => 1 bit is set => index is 1.
165
166 * bitmap & ((1 << 4) - 1) == 0b0 => 0 bits are set => index is 0.
167
168
169To conclude: Bitmap nodes are just like Array nodes -- they can store
170a number of pointers, but use bitmap compression to eliminate unused
171pointers.
172
173
174Bitmap nodes have two pointers for each item:
175
176 +----+----+----+----+ -- +----+----+
177 | k1 | v1 | k2 | v2 | .. | kN | vN |
178 +----+----+----+----+ -- +----+----+
179
180When kI == NULL, vI points to another tree level.
181
182When kI != NULL, the actual key object is stored in kI, and its
183value is stored in vI.
184
185
186Collision Nodes
187---------------
188
189Collision nodes are simple arrays of pointers -- two pointers per
190key/value. When there's a hash collision, say for k1/v1 and k2/v2
191we have `hash(k1)==hash(k2)`. Then our collision node will be:
192
193 +----+----+----+----+
194 | k1 | v1 | k2 | v2 |
195 +----+----+----+----+
196
197
198Tree Structure
199--------------
200
201All nodes are PyObjects.
202
203The `PyHamtObject` object has a pointer to the root node (h_root),
204and has a length field (h_count).
205
206High-level functions accept a PyHamtObject object and dispatch to
207lower-level functions depending on what kind of node h_root points to.
208
209
210Operations
211==========
212
213There are three fundamental operations on an immutable dictionary:
214
2151. "o.assoc(k, v)" will return a new immutable dictionary, that will be
216 a copy of "o", but with the "k/v" item set.
217
218 Functions in this file:
219
220 hamt_node_assoc, hamt_node_bitmap_assoc,
221 hamt_node_array_assoc, hamt_node_collision_assoc
222
223 `hamt_node_assoc` function accepts a node object, and calls
224 other functions depending on its actual type.
225
2262. "o.find(k)" will lookup key "k" in "o".
227
228 Functions:
229
230 hamt_node_find, hamt_node_bitmap_find,
231 hamt_node_array_find, hamt_node_collision_find
232
2333. "o.without(k)" will return a new immutable dictionary, that will be
234 a copy of "o", buth without the "k" key.
235
236 Functions:
237
238 hamt_node_without, hamt_node_bitmap_without,
239 hamt_node_array_without, hamt_node_collision_without
240
241
242Further Reading
243===============
244
2451. http://blog.higher-order.net/2009/09/08/understanding-clojures-persistenthashmap-deftwice.html
246
2472. http://blog.higher-order.net/2010/08/16/assoc-and-clojures-persistenthashmap-part-ii.html
248
2493. Clojure's PersistentHashMap implementation:
250 https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/PersistentHashMap.java
251
252
253Debug
254=====
255
256The HAMT datatype is accessible for testing purposes under the
257`_testcapi` module:
258
259 >>> from _testcapi import hamt
260 >>> h = hamt()
261 >>> h2 = h.set('a', 2)
262 >>> h3 = h2.set('b', 3)
263 >>> list(h3)
264 ['a', 'b']
265
266When CPython is built in debug mode, a '__dump__()' method is available
267to introspect the tree:
268
269 >>> print(h3.__dump__())
270 HAMT(len=2):
271 BitmapNode(size=4 count=2 bitmap=0b110 id=0x10eb9d9e8):
272 'a': 2
273 'b': 3
274*/
275
276
277#define IS_ARRAY_NODE(node) (Py_TYPE(node) == &_PyHamt_ArrayNode_Type)
278#define IS_BITMAP_NODE(node) (Py_TYPE(node) == &_PyHamt_BitmapNode_Type)
279#define IS_COLLISION_NODE(node) (Py_TYPE(node) == &_PyHamt_CollisionNode_Type)
280
281
282/* Return type for 'find' (lookup a key) functions.
283
284 * F_ERROR - an error occurred;
285 * F_NOT_FOUND - the key was not found;
286 * F_FOUND - the key was found.
287*/
288typedef enum {F_ERROR, F_NOT_FOUND, F_FOUND} hamt_find_t;
289
290
291/* Return type for 'without' (delete a key) functions.
292
293 * W_ERROR - an error occurred;
294 * W_NOT_FOUND - the key was not found: there's nothing to delete;
295 * W_EMPTY - the key was found: the node/tree would be empty
296 if the key is deleted;
297 * W_NEWNODE - the key was found: a new node/tree is returned
298 without that key.
299*/
300typedef enum {W_ERROR, W_NOT_FOUND, W_EMPTY, W_NEWNODE} hamt_without_t;
301
302
303/* Low-level iterator protocol type.
304
305 * I_ITEM - a new item has been yielded;
306 * I_END - the whole tree was visited (similar to StopIteration).
307*/
308typedef enum {I_ITEM, I_END} hamt_iter_t;
309
310
311#define HAMT_ARRAY_NODE_SIZE 32
312
313
314typedef struct {
315 PyObject_HEAD
316 PyHamtNode *a_array[HAMT_ARRAY_NODE_SIZE];
317 Py_ssize_t a_count;
318} PyHamtNode_Array;
319
320
321typedef struct {
322 PyObject_VAR_HEAD
323 uint32_t b_bitmap;
324 PyObject *b_array[1];
325} PyHamtNode_Bitmap;
326
327
328typedef struct {
329 PyObject_VAR_HEAD
330 int32_t c_hash;
331 PyObject *c_array[1];
332} PyHamtNode_Collision;
333
334
335static PyHamtNode_Bitmap *_empty_bitmap_node;
336static PyHamtObject *_empty_hamt;
337
338
339static PyHamtObject *
340hamt_alloc(void);
341
342static PyHamtNode *
343hamt_node_assoc(PyHamtNode *node,
344 uint32_t shift, int32_t hash,
345 PyObject *key, PyObject *val, int* added_leaf);
346
347static hamt_without_t
348hamt_node_without(PyHamtNode *node,
349 uint32_t shift, int32_t hash,
350 PyObject *key,
351 PyHamtNode **new_node);
352
353static hamt_find_t
354hamt_node_find(PyHamtNode *node,
355 uint32_t shift, int32_t hash,
356 PyObject *key, PyObject **val);
357
358#ifdef Py_DEBUG
359static int
360hamt_node_dump(PyHamtNode *node,
361 _PyUnicodeWriter *writer, int level);
362#endif
363
364static PyHamtNode *
365hamt_node_array_new(Py_ssize_t);
366
367static PyHamtNode *
368hamt_node_collision_new(int32_t hash, Py_ssize_t size);
369
370static inline Py_ssize_t
371hamt_node_collision_count(PyHamtNode_Collision *node);
372
373
374#ifdef Py_DEBUG
375static void
Victor Stinnera42de742018-11-22 10:25:22 +0100376_hamt_node_array_validate(void *obj_raw)
Yury Selivanovf23746a2018-01-22 19:11:18 -0500377{
Victor Stinnera42de742018-11-22 10:25:22 +0100378 PyObject *obj = _PyObject_CAST(obj_raw);
379 assert(IS_ARRAY_NODE(obj));
380 PyHamtNode_Array *node = (PyHamtNode_Array*)obj;
Yury Selivanovf23746a2018-01-22 19:11:18 -0500381 Py_ssize_t i = 0, count = 0;
382 for (; i < HAMT_ARRAY_NODE_SIZE; i++) {
383 if (node->a_array[i] != NULL) {
384 count++;
385 }
386 }
387 assert(count == node->a_count);
388}
389
390#define VALIDATE_ARRAY_NODE(NODE) \
391 do { _hamt_node_array_validate(NODE); } while (0);
392#else
393#define VALIDATE_ARRAY_NODE(NODE)
394#endif
395
396
397/* Returns -1 on error */
398static inline int32_t
399hamt_hash(PyObject *o)
400{
401 Py_hash_t hash = PyObject_Hash(o);
402
403#if SIZEOF_PY_HASH_T <= 4
404 return hash;
405#else
406 if (hash == -1) {
407 /* exception */
408 return -1;
409 }
410
411 /* While it's suboptimal to reduce Python's 64 bit hash to
412 32 bits via XOR, it seems that the resulting hash function
413 is good enough (this is also how Long type is hashed in Java.)
414 Storing 10, 100, 1000 Python strings results in a relatively
415 shallow and uniform tree structure.
416
417 Please don't change this hashing algorithm, as there are many
418 tests that test some exact tree shape to cover all code paths.
419 */
420 int32_t xored = (int32_t)(hash & 0xffffffffl) ^ (int32_t)(hash >> 32);
421 return xored == -1 ? -2 : xored;
422#endif
423}
424
425static inline uint32_t
426hamt_mask(int32_t hash, uint32_t shift)
427{
428 return (((uint32_t)hash >> shift) & 0x01f);
429}
430
431static inline uint32_t
432hamt_bitpos(int32_t hash, uint32_t shift)
433{
434 return (uint32_t)1 << hamt_mask(hash, shift);
435}
436
437static inline uint32_t
438hamt_bitcount(uint32_t i)
439{
Yury Selivanovb7a80d52018-01-23 22:17:04 -0500440 /* We could use native popcount instruction but that would
441 require to either add configure flags to enable SSE4.2
442 support or to detect it dynamically. Otherwise, we have
443 a risk of CPython not working properly on older hardware.
444
445 In practice, there's no observable difference in
446 performance between using a popcount instruction or the
447 following fallback code.
448
449 The algorithm is copied from:
450 https://graphics.stanford.edu/~seander/bithacks.html
451 */
Yury Selivanovf23746a2018-01-22 19:11:18 -0500452 i = i - ((i >> 1) & 0x55555555);
453 i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
Yury Selivanov6ab62922018-01-25 14:18:55 -0500454 return (((i + (i >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
Yury Selivanovf23746a2018-01-22 19:11:18 -0500455}
456
457static inline uint32_t
458hamt_bitindex(uint32_t bitmap, uint32_t bit)
459{
460 return hamt_bitcount(bitmap & (bit - 1));
461}
462
463
464/////////////////////////////////// Dump Helpers
465#ifdef Py_DEBUG
466
467static int
468_hamt_dump_ident(_PyUnicodeWriter *writer, int level)
469{
470 /* Write `' ' * level` to the `writer` */
471 PyObject *str = NULL;
472 PyObject *num = NULL;
473 PyObject *res = NULL;
474 int ret = -1;
475
476 str = PyUnicode_FromString(" ");
477 if (str == NULL) {
478 goto error;
479 }
480
481 num = PyLong_FromLong((long)level);
482 if (num == NULL) {
483 goto error;
484 }
485
486 res = PyNumber_Multiply(str, num);
487 if (res == NULL) {
488 goto error;
489 }
490
491 ret = _PyUnicodeWriter_WriteStr(writer, res);
492
493error:
494 Py_XDECREF(res);
495 Py_XDECREF(str);
496 Py_XDECREF(num);
497 return ret;
498}
499
500static int
501_hamt_dump_format(_PyUnicodeWriter *writer, const char *format, ...)
502{
503 /* A convenient helper combining _PyUnicodeWriter_WriteStr and
504 PyUnicode_FromFormatV.
505 */
506 PyObject* msg;
507 int ret;
508
509 va_list vargs;
510#ifdef HAVE_STDARG_PROTOTYPES
511 va_start(vargs, format);
512#else
513 va_start(vargs);
514#endif
515 msg = PyUnicode_FromFormatV(format, vargs);
516 va_end(vargs);
517
518 if (msg == NULL) {
519 return -1;
520 }
521
522 ret = _PyUnicodeWriter_WriteStr(writer, msg);
523 Py_DECREF(msg);
524 return ret;
525}
526
527#endif /* Py_DEBUG */
528/////////////////////////////////// Bitmap Node
529
530
531static PyHamtNode *
532hamt_node_bitmap_new(Py_ssize_t size)
533{
534 /* Create a new bitmap node of size 'size' */
535
536 PyHamtNode_Bitmap *node;
537 Py_ssize_t i;
538
539 assert(size >= 0);
540 assert(size % 2 == 0);
541
542 if (size == 0 && _empty_bitmap_node != NULL) {
543 Py_INCREF(_empty_bitmap_node);
544 return (PyHamtNode *)_empty_bitmap_node;
545 }
546
547 /* No freelist; allocate a new bitmap node */
548 node = PyObject_GC_NewVar(
549 PyHamtNode_Bitmap, &_PyHamt_BitmapNode_Type, size);
550 if (node == NULL) {
551 return NULL;
552 }
553
554 Py_SIZE(node) = size;
555
556 for (i = 0; i < size; i++) {
557 node->b_array[i] = NULL;
558 }
559
560 node->b_bitmap = 0;
561
562 _PyObject_GC_TRACK(node);
563
564 if (size == 0 && _empty_bitmap_node == NULL) {
565 /* Since bitmap nodes are immutable, we can cache the instance
566 for size=0 and reuse it whenever we need an empty bitmap node.
567 */
568 _empty_bitmap_node = node;
569 Py_INCREF(_empty_bitmap_node);
570 }
571
572 return (PyHamtNode *)node;
573}
574
575static inline Py_ssize_t
576hamt_node_bitmap_count(PyHamtNode_Bitmap *node)
577{
578 return Py_SIZE(node) / 2;
579}
580
581static PyHamtNode_Bitmap *
582hamt_node_bitmap_clone(PyHamtNode_Bitmap *node)
583{
584 /* Clone a bitmap node; return a new one with the same child notes. */
585
586 PyHamtNode_Bitmap *clone;
587 Py_ssize_t i;
588
589 clone = (PyHamtNode_Bitmap *)hamt_node_bitmap_new(Py_SIZE(node));
590 if (clone == NULL) {
591 return NULL;
592 }
593
594 for (i = 0; i < Py_SIZE(node); i++) {
595 Py_XINCREF(node->b_array[i]);
596 clone->b_array[i] = node->b_array[i];
597 }
598
599 clone->b_bitmap = node->b_bitmap;
600 return clone;
601}
602
603static PyHamtNode_Bitmap *
604hamt_node_bitmap_clone_without(PyHamtNode_Bitmap *o, uint32_t bit)
605{
606 assert(bit & o->b_bitmap);
607 assert(hamt_node_bitmap_count(o) > 1);
608
609 PyHamtNode_Bitmap *new = (PyHamtNode_Bitmap *)hamt_node_bitmap_new(
610 Py_SIZE(o) - 2);
611 if (new == NULL) {
612 return NULL;
613 }
614
615 uint32_t idx = hamt_bitindex(o->b_bitmap, bit);
616 uint32_t key_idx = 2 * idx;
617 uint32_t val_idx = key_idx + 1;
618 uint32_t i;
619
620 for (i = 0; i < key_idx; i++) {
621 Py_XINCREF(o->b_array[i]);
622 new->b_array[i] = o->b_array[i];
623 }
624
Yury Selivanovb647d702018-01-29 13:31:37 -0500625 assert(Py_SIZE(o) >= 0 && Py_SIZE(o) <= 32);
626 for (i = val_idx + 1; i < (uint32_t)Py_SIZE(o); i++) {
Yury Selivanovf23746a2018-01-22 19:11:18 -0500627 Py_XINCREF(o->b_array[i]);
628 new->b_array[i - 2] = o->b_array[i];
629 }
630
631 new->b_bitmap = o->b_bitmap & ~bit;
632 return new;
633}
634
635static PyHamtNode *
636hamt_node_new_bitmap_or_collision(uint32_t shift,
637 PyObject *key1, PyObject *val1,
638 int32_t key2_hash,
639 PyObject *key2, PyObject *val2)
640{
641 /* Helper method. Creates a new node for key1/val and key2/val2
642 pairs.
643
644 If key1 hash is equal to the hash of key2, a Collision node
645 will be created. If they are not equal, a Bitmap node is
646 created.
647 */
648
649 int32_t key1_hash = hamt_hash(key1);
650 if (key1_hash == -1) {
651 return NULL;
652 }
653
654 if (key1_hash == key2_hash) {
655 PyHamtNode_Collision *n;
656 n = (PyHamtNode_Collision *)hamt_node_collision_new(key1_hash, 4);
657 if (n == NULL) {
658 return NULL;
659 }
660
661 Py_INCREF(key1);
662 n->c_array[0] = key1;
663 Py_INCREF(val1);
664 n->c_array[1] = val1;
665
666 Py_INCREF(key2);
667 n->c_array[2] = key2;
668 Py_INCREF(val2);
669 n->c_array[3] = val2;
670
671 return (PyHamtNode *)n;
672 }
673 else {
674 int added_leaf = 0;
675 PyHamtNode *n = hamt_node_bitmap_new(0);
676 if (n == NULL) {
677 return NULL;
678 }
679
680 PyHamtNode *n2 = hamt_node_assoc(
681 n, shift, key1_hash, key1, val1, &added_leaf);
682 Py_DECREF(n);
683 if (n2 == NULL) {
684 return NULL;
685 }
686
687 n = hamt_node_assoc(n2, shift, key2_hash, key2, val2, &added_leaf);
688 Py_DECREF(n2);
689 if (n == NULL) {
690 return NULL;
691 }
692
693 return n;
694 }
695}
696
697static PyHamtNode *
698hamt_node_bitmap_assoc(PyHamtNode_Bitmap *self,
699 uint32_t shift, int32_t hash,
700 PyObject *key, PyObject *val, int* added_leaf)
701{
702 /* assoc operation for bitmap nodes.
703
704 Return: a new node, or self if key/val already is in the
705 collection.
706
707 'added_leaf' is later used in '_PyHamt_Assoc' to determine if
708 `hamt.set(key, val)` increased the size of the collection.
709 */
710
711 uint32_t bit = hamt_bitpos(hash, shift);
712 uint32_t idx = hamt_bitindex(self->b_bitmap, bit);
713
714 /* Bitmap node layout:
715
716 +------+------+------+------+ --- +------+------+
717 | key1 | val1 | key2 | val2 | ... | keyN | valN |
718 +------+------+------+------+ --- +------+------+
719 where `N < Py_SIZE(node)`.
720
721 The `node->b_bitmap` field is a bitmap. For a given
722 `(shift, hash)` pair we can determine:
723
724 - If this node has the corresponding key/val slots.
725 - The index of key/val slots.
726 */
727
728 if (self->b_bitmap & bit) {
729 /* The key is set in this node */
730
731 uint32_t key_idx = 2 * idx;
732 uint32_t val_idx = key_idx + 1;
733
Serhiy Storchakabfe4fd52018-02-09 17:31:26 +0200734 assert(val_idx < (size_t)Py_SIZE(self));
Yury Selivanovf23746a2018-01-22 19:11:18 -0500735
736 PyObject *key_or_null = self->b_array[key_idx];
737 PyObject *val_or_node = self->b_array[val_idx];
738
739 if (key_or_null == NULL) {
740 /* key is NULL. This means that we have a few keys
741 that have the same (hash, shift) pair. */
742
743 assert(val_or_node != NULL);
744
745 PyHamtNode *sub_node = hamt_node_assoc(
746 (PyHamtNode *)val_or_node,
747 shift + 5, hash, key, val, added_leaf);
748 if (sub_node == NULL) {
749 return NULL;
750 }
751
752 if (val_or_node == (PyObject *)sub_node) {
753 Py_DECREF(sub_node);
754 Py_INCREF(self);
755 return (PyHamtNode *)self;
756 }
757
758 PyHamtNode_Bitmap *ret = hamt_node_bitmap_clone(self);
759 if (ret == NULL) {
760 return NULL;
761 }
762 Py_SETREF(ret->b_array[val_idx], (PyObject*)sub_node);
763 return (PyHamtNode *)ret;
764 }
765
766 assert(key != NULL);
767 /* key is not NULL. This means that we have only one other
768 key in this collection that matches our hash for this shift. */
769
770 int comp_err = PyObject_RichCompareBool(key, key_or_null, Py_EQ);
771 if (comp_err < 0) { /* exception in __eq__ */
772 return NULL;
773 }
774 if (comp_err == 1) { /* key == key_or_null */
775 if (val == val_or_node) {
776 /* we already have the same key/val pair; return self. */
777 Py_INCREF(self);
778 return (PyHamtNode *)self;
779 }
780
781 /* We're setting a new value for the key we had before.
782 Make a new bitmap node with a replaced value, and return it. */
783 PyHamtNode_Bitmap *ret = hamt_node_bitmap_clone(self);
784 if (ret == NULL) {
785 return NULL;
786 }
787 Py_INCREF(val);
788 Py_SETREF(ret->b_array[val_idx], val);
789 return (PyHamtNode *)ret;
790 }
791
792 /* It's a new key, and it has the same index as *one* another key.
793 We have a collision. We need to create a new node which will
794 combine the existing key and the key we're adding.
795
796 `hamt_node_new_bitmap_or_collision` will either create a new
797 Collision node if the keys have identical hashes, or
798 a new Bitmap node.
799 */
800 PyHamtNode *sub_node = hamt_node_new_bitmap_or_collision(
801 shift + 5,
802 key_or_null, val_or_node, /* existing key/val */
803 hash,
804 key, val /* new key/val */
805 );
806 if (sub_node == NULL) {
807 return NULL;
808 }
809
810 PyHamtNode_Bitmap *ret = hamt_node_bitmap_clone(self);
811 if (ret == NULL) {
812 Py_DECREF(sub_node);
813 return NULL;
814 }
815 Py_SETREF(ret->b_array[key_idx], NULL);
816 Py_SETREF(ret->b_array[val_idx], (PyObject *)sub_node);
817
818 *added_leaf = 1;
819 return (PyHamtNode *)ret;
820 }
821 else {
822 /* There was no key before with the same (shift,hash). */
823
824 uint32_t n = hamt_bitcount(self->b_bitmap);
825
826 if (n >= 16) {
827 /* When we have a situation where we want to store more
828 than 16 nodes at one level of the tree, we no longer
829 want to use the Bitmap node with bitmap encoding.
830
831 Instead we start using an Array node, which has
832 simpler (faster) implementation at the expense of
833 having prealocated 32 pointers for its keys/values
834 pairs.
835
836 Small hamt objects (<30 keys) usually don't have any
Ville Skyttä61f82e02018-04-20 23:08:45 +0300837 Array nodes at all. Between ~30 and ~400 keys hamt
Yury Selivanovf23746a2018-01-22 19:11:18 -0500838 objects usually have one Array node, and usually it's
839 a root node.
840 */
841
842 uint32_t jdx = hamt_mask(hash, shift);
843 /* 'jdx' is the index of where the new key should be added
844 in the new Array node we're about to create. */
845
846 PyHamtNode *empty = NULL;
847 PyHamtNode_Array *new_node = NULL;
848 PyHamtNode *res = NULL;
849
850 /* Create a new Array node. */
851 new_node = (PyHamtNode_Array *)hamt_node_array_new(n + 1);
852 if (new_node == NULL) {
853 goto fin;
854 }
855
856 /* Create an empty bitmap node for the next
857 hamt_node_assoc call. */
858 empty = hamt_node_bitmap_new(0);
859 if (empty == NULL) {
860 goto fin;
861 }
862
863 /* Make a new bitmap node for the key/val we're adding.
864 Set that bitmap node to new-array-node[jdx]. */
865 new_node->a_array[jdx] = hamt_node_assoc(
866 empty, shift + 5, hash, key, val, added_leaf);
867 if (new_node->a_array[jdx] == NULL) {
868 goto fin;
869 }
870
871 /* Copy existing key/value pairs from the current Bitmap
872 node to the new Array node we've just created. */
873 Py_ssize_t i, j;
874 for (i = 0, j = 0; i < HAMT_ARRAY_NODE_SIZE; i++) {
875 if (((self->b_bitmap >> i) & 1) != 0) {
876 /* Ensure we don't accidentally override `jdx` element
877 we set few lines above.
878 */
879 assert(new_node->a_array[i] == NULL);
880
881 if (self->b_array[j] == NULL) {
882 new_node->a_array[i] =
883 (PyHamtNode *)self->b_array[j + 1];
884 Py_INCREF(new_node->a_array[i]);
885 }
886 else {
887 int32_t rehash = hamt_hash(self->b_array[j]);
888 if (rehash == -1) {
889 goto fin;
890 }
891
892 new_node->a_array[i] = hamt_node_assoc(
893 empty, shift + 5,
894 rehash,
895 self->b_array[j],
896 self->b_array[j + 1],
897 added_leaf);
898
899 if (new_node->a_array[i] == NULL) {
900 goto fin;
901 }
902 }
903 j += 2;
904 }
905 }
906
907 VALIDATE_ARRAY_NODE(new_node)
908
909 /* That's it! */
910 res = (PyHamtNode *)new_node;
911
912 fin:
913 Py_XDECREF(empty);
914 if (res == NULL) {
915 Py_XDECREF(new_node);
916 }
917 return res;
918 }
919 else {
920 /* We have less than 16 keys at this level; let's just
921 create a new bitmap node out of this node with the
922 new key/val pair added. */
923
924 uint32_t key_idx = 2 * idx;
925 uint32_t val_idx = key_idx + 1;
Yury Selivanovb647d702018-01-29 13:31:37 -0500926 uint32_t i;
Yury Selivanovf23746a2018-01-22 19:11:18 -0500927
928 *added_leaf = 1;
929
930 /* Allocate new Bitmap node which can have one more key/val
931 pair in addition to what we have already. */
932 PyHamtNode_Bitmap *new_node =
933 (PyHamtNode_Bitmap *)hamt_node_bitmap_new(2 * (n + 1));
934 if (new_node == NULL) {
935 return NULL;
936 }
937
938 /* Copy all keys/values that will be before the new key/value
939 we are adding. */
940 for (i = 0; i < key_idx; i++) {
941 Py_XINCREF(self->b_array[i]);
942 new_node->b_array[i] = self->b_array[i];
943 }
944
945 /* Set the new key/value to the new Bitmap node. */
946 Py_INCREF(key);
947 new_node->b_array[key_idx] = key;
948 Py_INCREF(val);
949 new_node->b_array[val_idx] = val;
950
951 /* Copy all keys/values that will be after the new key/value
952 we are adding. */
Yury Selivanovb647d702018-01-29 13:31:37 -0500953 assert(Py_SIZE(self) >= 0 && Py_SIZE(self) <= 32);
954 for (i = key_idx; i < (uint32_t)Py_SIZE(self); i++) {
Yury Selivanovf23746a2018-01-22 19:11:18 -0500955 Py_XINCREF(self->b_array[i]);
956 new_node->b_array[i + 2] = self->b_array[i];
957 }
958
959 new_node->b_bitmap = self->b_bitmap | bit;
960 return (PyHamtNode *)new_node;
961 }
962 }
963}
964
965static hamt_without_t
966hamt_node_bitmap_without(PyHamtNode_Bitmap *self,
967 uint32_t shift, int32_t hash,
968 PyObject *key,
969 PyHamtNode **new_node)
970{
971 uint32_t bit = hamt_bitpos(hash, shift);
972 if ((self->b_bitmap & bit) == 0) {
973 return W_NOT_FOUND;
974 }
975
976 uint32_t idx = hamt_bitindex(self->b_bitmap, bit);
977
978 uint32_t key_idx = 2 * idx;
979 uint32_t val_idx = key_idx + 1;
980
981 PyObject *key_or_null = self->b_array[key_idx];
982 PyObject *val_or_node = self->b_array[val_idx];
983
984 if (key_or_null == NULL) {
985 /* key == NULL means that 'value' is another tree node. */
986
987 PyHamtNode *sub_node = NULL;
988
989 hamt_without_t res = hamt_node_without(
990 (PyHamtNode *)val_or_node,
991 shift + 5, hash, key, &sub_node);
992
993 switch (res) {
994 case W_EMPTY:
995 /* It's impossible for us to receive a W_EMPTY here:
996
997 - Array nodes are converted to Bitmap nodes when
998 we delete 16th item from them;
999
1000 - Collision nodes are converted to Bitmap when
1001 there is one item in them;
1002
1003 - Bitmap node's without() inlines single-item
1004 sub-nodes.
1005
1006 So in no situation we can have a single-item
1007 Bitmap child of another Bitmap node.
1008 */
1009 Py_UNREACHABLE();
1010
1011 case W_NEWNODE: {
1012 assert(sub_node != NULL);
1013
1014 if (IS_BITMAP_NODE(sub_node)) {
1015 PyHamtNode_Bitmap *sub_tree = (PyHamtNode_Bitmap *)sub_node;
1016 if (hamt_node_bitmap_count(sub_tree) == 1 &&
1017 sub_tree->b_array[0] != NULL)
1018 {
1019 /* A bitmap node with one key/value pair. Just
1020 merge it into this node.
1021
1022 Note that we don't inline Bitmap nodes that
1023 have a NULL key -- those nodes point to another
1024 tree level, and we cannot simply move tree levels
1025 up or down.
1026 */
1027
1028 PyHamtNode_Bitmap *clone = hamt_node_bitmap_clone(self);
1029 if (clone == NULL) {
1030 Py_DECREF(sub_node);
1031 return W_ERROR;
1032 }
1033
1034 PyObject *key = sub_tree->b_array[0];
1035 PyObject *val = sub_tree->b_array[1];
1036
1037 Py_INCREF(key);
1038 Py_XSETREF(clone->b_array[key_idx], key);
1039 Py_INCREF(val);
1040 Py_SETREF(clone->b_array[val_idx], val);
1041
1042 Py_DECREF(sub_tree);
1043
1044 *new_node = (PyHamtNode *)clone;
1045 return W_NEWNODE;
1046 }
1047 }
1048
1049#ifdef Py_DEBUG
1050 /* Ensure that Collision.without implementation
1051 converts to Bitmap nodes itself.
1052 */
1053 if (IS_COLLISION_NODE(sub_node)) {
1054 assert(hamt_node_collision_count(
1055 (PyHamtNode_Collision*)sub_node) > 1);
1056 }
1057#endif
1058
1059 PyHamtNode_Bitmap *clone = hamt_node_bitmap_clone(self);
Yury Selivanov0bad4d62018-01-23 16:26:07 -05001060 if (clone == NULL) {
1061 return W_ERROR;
1062 }
1063
Yury Selivanovf23746a2018-01-22 19:11:18 -05001064 Py_SETREF(clone->b_array[val_idx],
1065 (PyObject *)sub_node); /* borrow */
1066
1067 *new_node = (PyHamtNode *)clone;
1068 return W_NEWNODE;
1069 }
1070
1071 case W_ERROR:
1072 case W_NOT_FOUND:
1073 assert(sub_node == NULL);
1074 return res;
1075
1076 default:
1077 Py_UNREACHABLE();
1078 }
1079 }
1080 else {
1081 /* We have a regular key/value pair */
1082
1083 int cmp = PyObject_RichCompareBool(key_or_null, key, Py_EQ);
1084 if (cmp < 0) {
1085 return W_ERROR;
1086 }
1087 if (cmp == 0) {
1088 return W_NOT_FOUND;
1089 }
1090
1091 if (hamt_node_bitmap_count(self) == 1) {
1092 return W_EMPTY;
1093 }
1094
1095 *new_node = (PyHamtNode *)
1096 hamt_node_bitmap_clone_without(self, bit);
1097 if (*new_node == NULL) {
1098 return W_ERROR;
1099 }
1100
1101 return W_NEWNODE;
1102 }
1103}
1104
1105static hamt_find_t
1106hamt_node_bitmap_find(PyHamtNode_Bitmap *self,
1107 uint32_t shift, int32_t hash,
1108 PyObject *key, PyObject **val)
1109{
1110 /* Lookup a key in a Bitmap node. */
1111
1112 uint32_t bit = hamt_bitpos(hash, shift);
1113 uint32_t idx;
1114 uint32_t key_idx;
1115 uint32_t val_idx;
1116 PyObject *key_or_null;
1117 PyObject *val_or_node;
1118 int comp_err;
1119
1120 if ((self->b_bitmap & bit) == 0) {
1121 return F_NOT_FOUND;
1122 }
1123
1124 idx = hamt_bitindex(self->b_bitmap, bit);
Yury Selivanovf23746a2018-01-22 19:11:18 -05001125 key_idx = idx * 2;
1126 val_idx = key_idx + 1;
1127
Serhiy Storchakabfe4fd52018-02-09 17:31:26 +02001128 assert(val_idx < (size_t)Py_SIZE(self));
Yury Selivanovf23746a2018-01-22 19:11:18 -05001129
1130 key_or_null = self->b_array[key_idx];
1131 val_or_node = self->b_array[val_idx];
1132
1133 if (key_or_null == NULL) {
1134 /* There are a few keys that have the same hash at the current shift
1135 that match our key. Dispatch the lookup further down the tree. */
1136 assert(val_or_node != NULL);
1137 return hamt_node_find((PyHamtNode *)val_or_node,
1138 shift + 5, hash, key, val);
1139 }
1140
1141 /* We have only one key -- a potential match. Let's compare if the
1142 key we are looking at is equal to the key we are looking for. */
1143 assert(key != NULL);
1144 comp_err = PyObject_RichCompareBool(key, key_or_null, Py_EQ);
1145 if (comp_err < 0) { /* exception in __eq__ */
1146 return F_ERROR;
1147 }
1148 if (comp_err == 1) { /* key == key_or_null */
1149 *val = val_or_node;
1150 return F_FOUND;
1151 }
1152
1153 return F_NOT_FOUND;
1154}
1155
1156static int
1157hamt_node_bitmap_traverse(PyHamtNode_Bitmap *self, visitproc visit, void *arg)
1158{
1159 /* Bitmap's tp_traverse */
1160
1161 Py_ssize_t i;
1162
1163 for (i = Py_SIZE(self); --i >= 0; ) {
1164 Py_VISIT(self->b_array[i]);
1165 }
1166
1167 return 0;
1168}
1169
1170static void
1171hamt_node_bitmap_dealloc(PyHamtNode_Bitmap *self)
1172{
1173 /* Bitmap's tp_dealloc */
1174
1175 Py_ssize_t len = Py_SIZE(self);
1176 Py_ssize_t i;
1177
1178 PyObject_GC_UnTrack(self);
Jeroen Demeyer351c6742019-05-10 19:21:11 +02001179 Py_TRASHCAN_BEGIN(self, hamt_node_bitmap_dealloc)
Yury Selivanovf23746a2018-01-22 19:11:18 -05001180
1181 if (len > 0) {
1182 i = len;
1183 while (--i >= 0) {
1184 Py_XDECREF(self->b_array[i]);
1185 }
1186 }
1187
1188 Py_TYPE(self)->tp_free((PyObject *)self);
Jeroen Demeyer351c6742019-05-10 19:21:11 +02001189 Py_TRASHCAN_END
Yury Selivanovf23746a2018-01-22 19:11:18 -05001190}
1191
1192#ifdef Py_DEBUG
1193static int
1194hamt_node_bitmap_dump(PyHamtNode_Bitmap *node,
1195 _PyUnicodeWriter *writer, int level)
1196{
1197 /* Debug build: __dump__() method implementation for Bitmap nodes. */
1198
1199 Py_ssize_t i;
1200 PyObject *tmp1;
1201 PyObject *tmp2;
1202
1203 if (_hamt_dump_ident(writer, level + 1)) {
1204 goto error;
1205 }
1206
1207 if (_hamt_dump_format(writer, "BitmapNode(size=%zd count=%zd ",
1208 Py_SIZE(node), Py_SIZE(node) / 2))
1209 {
1210 goto error;
1211 }
1212
1213 tmp1 = PyLong_FromUnsignedLong(node->b_bitmap);
1214 if (tmp1 == NULL) {
1215 goto error;
1216 }
1217 tmp2 = _PyLong_Format(tmp1, 2);
1218 Py_DECREF(tmp1);
1219 if (tmp2 == NULL) {
1220 goto error;
1221 }
1222 if (_hamt_dump_format(writer, "bitmap=%S id=%p):\n", tmp2, node)) {
1223 Py_DECREF(tmp2);
1224 goto error;
1225 }
1226 Py_DECREF(tmp2);
1227
1228 for (i = 0; i < Py_SIZE(node); i += 2) {
1229 PyObject *key_or_null = node->b_array[i];
1230 PyObject *val_or_node = node->b_array[i + 1];
1231
1232 if (_hamt_dump_ident(writer, level + 2)) {
1233 goto error;
1234 }
1235
1236 if (key_or_null == NULL) {
1237 if (_hamt_dump_format(writer, "NULL:\n")) {
1238 goto error;
1239 }
1240
1241 if (hamt_node_dump((PyHamtNode *)val_or_node,
1242 writer, level + 2))
1243 {
1244 goto error;
1245 }
1246 }
1247 else {
1248 if (_hamt_dump_format(writer, "%R: %R", key_or_null,
1249 val_or_node))
1250 {
1251 goto error;
1252 }
1253 }
1254
1255 if (_hamt_dump_format(writer, "\n")) {
1256 goto error;
1257 }
1258 }
1259
1260 return 0;
1261error:
1262 return -1;
1263}
1264#endif /* Py_DEBUG */
1265
1266
1267/////////////////////////////////// Collision Node
1268
1269
1270static PyHamtNode *
1271hamt_node_collision_new(int32_t hash, Py_ssize_t size)
1272{
1273 /* Create a new Collision node. */
1274
1275 PyHamtNode_Collision *node;
1276 Py_ssize_t i;
1277
1278 assert(size >= 4);
1279 assert(size % 2 == 0);
1280
1281 node = PyObject_GC_NewVar(
1282 PyHamtNode_Collision, &_PyHamt_CollisionNode_Type, size);
1283 if (node == NULL) {
1284 return NULL;
1285 }
1286
1287 for (i = 0; i < size; i++) {
1288 node->c_array[i] = NULL;
1289 }
1290
1291 Py_SIZE(node) = size;
1292 node->c_hash = hash;
1293
1294 _PyObject_GC_TRACK(node);
1295
1296 return (PyHamtNode *)node;
1297}
1298
1299static hamt_find_t
1300hamt_node_collision_find_index(PyHamtNode_Collision *self, PyObject *key,
1301 Py_ssize_t *idx)
1302{
1303 /* Lookup `key` in the Collision node `self`. Set the index of the
1304 found key to 'idx'. */
1305
1306 Py_ssize_t i;
1307 PyObject *el;
1308
1309 for (i = 0; i < Py_SIZE(self); i += 2) {
1310 el = self->c_array[i];
1311
1312 assert(el != NULL);
1313 int cmp = PyObject_RichCompareBool(key, el, Py_EQ);
1314 if (cmp < 0) {
1315 return F_ERROR;
1316 }
1317 if (cmp == 1) {
1318 *idx = i;
1319 return F_FOUND;
1320 }
1321 }
1322
1323 return F_NOT_FOUND;
1324}
1325
1326static PyHamtNode *
1327hamt_node_collision_assoc(PyHamtNode_Collision *self,
1328 uint32_t shift, int32_t hash,
1329 PyObject *key, PyObject *val, int* added_leaf)
1330{
1331 /* Set a new key to this level (currently a Collision node)
1332 of the tree. */
1333
1334 if (hash == self->c_hash) {
1335 /* The hash of the 'key' we are adding matches the hash of
1336 other keys in this Collision node. */
1337
1338 Py_ssize_t key_idx = -1;
1339 hamt_find_t found;
1340 PyHamtNode_Collision *new_node;
1341 Py_ssize_t i;
1342
1343 /* Let's try to lookup the new 'key', maybe we already have it. */
1344 found = hamt_node_collision_find_index(self, key, &key_idx);
1345 switch (found) {
1346 case F_ERROR:
1347 /* Exception. */
1348 return NULL;
1349
1350 case F_NOT_FOUND:
1351 /* This is a totally new key. Clone the current node,
1352 add a new key/value to the cloned node. */
1353
1354 new_node = (PyHamtNode_Collision *)hamt_node_collision_new(
1355 self->c_hash, Py_SIZE(self) + 2);
1356 if (new_node == NULL) {
1357 return NULL;
1358 }
1359
1360 for (i = 0; i < Py_SIZE(self); i++) {
1361 Py_INCREF(self->c_array[i]);
1362 new_node->c_array[i] = self->c_array[i];
1363 }
1364
1365 Py_INCREF(key);
1366 new_node->c_array[i] = key;
1367 Py_INCREF(val);
1368 new_node->c_array[i + 1] = val;
1369
1370 *added_leaf = 1;
1371 return (PyHamtNode *)new_node;
1372
1373 case F_FOUND:
1374 /* There's a key which is equal to the key we are adding. */
1375
1376 assert(key_idx >= 0);
1377 assert(key_idx < Py_SIZE(self));
1378 Py_ssize_t val_idx = key_idx + 1;
1379
1380 if (self->c_array[val_idx] == val) {
1381 /* We're setting a key/value pair that's already set. */
1382 Py_INCREF(self);
1383 return (PyHamtNode *)self;
1384 }
1385
1386 /* We need to replace old value for the key
1387 with a new value. Create a new Collision node.*/
1388 new_node = (PyHamtNode_Collision *)hamt_node_collision_new(
1389 self->c_hash, Py_SIZE(self));
1390 if (new_node == NULL) {
1391 return NULL;
1392 }
1393
1394 /* Copy all elements of the old node to the new one. */
1395 for (i = 0; i < Py_SIZE(self); i++) {
1396 Py_INCREF(self->c_array[i]);
1397 new_node->c_array[i] = self->c_array[i];
1398 }
1399
1400 /* Replace the old value with the new value for the our key. */
1401 Py_DECREF(new_node->c_array[val_idx]);
1402 Py_INCREF(val);
1403 new_node->c_array[val_idx] = val;
1404
1405 return (PyHamtNode *)new_node;
1406
1407 default:
1408 Py_UNREACHABLE();
1409 }
1410 }
1411 else {
1412 /* The hash of the new key is different from the hash that
1413 all keys of this Collision node have.
1414
1415 Create a Bitmap node inplace with two children:
1416 key/value pair that we're adding, and the Collision node
1417 we're replacing on this tree level.
1418 */
1419
1420 PyHamtNode_Bitmap *new_node;
1421 PyHamtNode *assoc_res;
1422
1423 new_node = (PyHamtNode_Bitmap *)hamt_node_bitmap_new(2);
1424 if (new_node == NULL) {
1425 return NULL;
1426 }
1427 new_node->b_bitmap = hamt_bitpos(self->c_hash, shift);
1428 Py_INCREF(self);
1429 new_node->b_array[1] = (PyObject*) self;
1430
1431 assoc_res = hamt_node_bitmap_assoc(
1432 new_node, shift, hash, key, val, added_leaf);
1433 Py_DECREF(new_node);
1434 return assoc_res;
1435 }
1436}
1437
1438static inline Py_ssize_t
1439hamt_node_collision_count(PyHamtNode_Collision *node)
1440{
1441 return Py_SIZE(node) / 2;
1442}
1443
1444static hamt_without_t
1445hamt_node_collision_without(PyHamtNode_Collision *self,
1446 uint32_t shift, int32_t hash,
1447 PyObject *key,
1448 PyHamtNode **new_node)
1449{
1450 if (hash != self->c_hash) {
1451 return W_NOT_FOUND;
1452 }
1453
1454 Py_ssize_t key_idx = -1;
1455 hamt_find_t found = hamt_node_collision_find_index(self, key, &key_idx);
1456
1457 switch (found) {
1458 case F_ERROR:
1459 return W_ERROR;
1460
1461 case F_NOT_FOUND:
1462 return W_NOT_FOUND;
1463
1464 case F_FOUND:
1465 assert(key_idx >= 0);
1466 assert(key_idx < Py_SIZE(self));
1467
1468 Py_ssize_t new_count = hamt_node_collision_count(self) - 1;
1469
1470 if (new_count == 0) {
1471 /* The node has only one key/value pair and it's for the
1472 key we're trying to delete. So a new node will be empty
1473 after the removal.
1474 */
1475 return W_EMPTY;
1476 }
1477
1478 if (new_count == 1) {
1479 /* The node has two keys, and after deletion the
1480 new Collision node would have one. Collision nodes
Dmitry Alimov01a0cb82018-02-02 05:59:48 +03001481 with one key shouldn't exist, so convert it to a
Yury Selivanovf23746a2018-01-22 19:11:18 -05001482 Bitmap node.
1483 */
1484 PyHamtNode_Bitmap *node = (PyHamtNode_Bitmap *)
1485 hamt_node_bitmap_new(2);
1486 if (node == NULL) {
1487 return W_ERROR;
1488 }
1489
1490 if (key_idx == 0) {
1491 Py_INCREF(self->c_array[2]);
1492 node->b_array[0] = self->c_array[2];
1493 Py_INCREF(self->c_array[3]);
1494 node->b_array[1] = self->c_array[3];
1495 }
1496 else {
1497 assert(key_idx == 2);
1498 Py_INCREF(self->c_array[0]);
1499 node->b_array[0] = self->c_array[0];
1500 Py_INCREF(self->c_array[1]);
1501 node->b_array[1] = self->c_array[1];
1502 }
1503
1504 node->b_bitmap = hamt_bitpos(hash, shift);
1505
1506 *new_node = (PyHamtNode *)node;
1507 return W_NEWNODE;
1508 }
1509
1510 /* Allocate a new Collision node with capacity for one
1511 less key/value pair */
1512 PyHamtNode_Collision *new = (PyHamtNode_Collision *)
1513 hamt_node_collision_new(
1514 self->c_hash, Py_SIZE(self) - 2);
Xiang Zhang3c7ac7e2018-03-08 13:59:46 +08001515 if (new == NULL) {
1516 return W_ERROR;
1517 }
Yury Selivanovf23746a2018-01-22 19:11:18 -05001518
1519 /* Copy all other keys from `self` to `new` */
1520 Py_ssize_t i;
1521 for (i = 0; i < key_idx; i++) {
1522 Py_INCREF(self->c_array[i]);
1523 new->c_array[i] = self->c_array[i];
1524 }
1525 for (i = key_idx + 2; i < Py_SIZE(self); i++) {
1526 Py_INCREF(self->c_array[i]);
1527 new->c_array[i - 2] = self->c_array[i];
1528 }
1529
1530 *new_node = (PyHamtNode*)new;
1531 return W_NEWNODE;
1532
1533 default:
1534 Py_UNREACHABLE();
1535 }
1536}
1537
1538static hamt_find_t
1539hamt_node_collision_find(PyHamtNode_Collision *self,
1540 uint32_t shift, int32_t hash,
1541 PyObject *key, PyObject **val)
1542{
1543 /* Lookup `key` in the Collision node `self`. Set the value
1544 for the found key to 'val'. */
1545
1546 Py_ssize_t idx = -1;
1547 hamt_find_t res;
1548
1549 res = hamt_node_collision_find_index(self, key, &idx);
1550 if (res == F_ERROR || res == F_NOT_FOUND) {
1551 return res;
1552 }
1553
1554 assert(idx >= 0);
1555 assert(idx + 1 < Py_SIZE(self));
1556
1557 *val = self->c_array[idx + 1];
1558 assert(*val != NULL);
1559
1560 return F_FOUND;
1561}
1562
1563
1564static int
1565hamt_node_collision_traverse(PyHamtNode_Collision *self,
1566 visitproc visit, void *arg)
1567{
1568 /* Collision's tp_traverse */
1569
1570 Py_ssize_t i;
1571
1572 for (i = Py_SIZE(self); --i >= 0; ) {
1573 Py_VISIT(self->c_array[i]);
1574 }
1575
1576 return 0;
1577}
1578
1579static void
1580hamt_node_collision_dealloc(PyHamtNode_Collision *self)
1581{
1582 /* Collision's tp_dealloc */
1583
1584 Py_ssize_t len = Py_SIZE(self);
1585
1586 PyObject_GC_UnTrack(self);
Jeroen Demeyer351c6742019-05-10 19:21:11 +02001587 Py_TRASHCAN_BEGIN(self, hamt_node_collision_dealloc)
Yury Selivanovf23746a2018-01-22 19:11:18 -05001588
1589 if (len > 0) {
1590
1591 while (--len >= 0) {
1592 Py_XDECREF(self->c_array[len]);
1593 }
1594 }
1595
1596 Py_TYPE(self)->tp_free((PyObject *)self);
Jeroen Demeyer351c6742019-05-10 19:21:11 +02001597 Py_TRASHCAN_END
Yury Selivanovf23746a2018-01-22 19:11:18 -05001598}
1599
1600#ifdef Py_DEBUG
1601static int
1602hamt_node_collision_dump(PyHamtNode_Collision *node,
1603 _PyUnicodeWriter *writer, int level)
1604{
1605 /* Debug build: __dump__() method implementation for Collision nodes. */
1606
1607 Py_ssize_t i;
1608
1609 if (_hamt_dump_ident(writer, level + 1)) {
1610 goto error;
1611 }
1612
1613 if (_hamt_dump_format(writer, "CollisionNode(size=%zd id=%p):\n",
1614 Py_SIZE(node), node))
1615 {
1616 goto error;
1617 }
1618
1619 for (i = 0; i < Py_SIZE(node); i += 2) {
1620 PyObject *key = node->c_array[i];
1621 PyObject *val = node->c_array[i + 1];
1622
1623 if (_hamt_dump_ident(writer, level + 2)) {
1624 goto error;
1625 }
1626
1627 if (_hamt_dump_format(writer, "%R: %R\n", key, val)) {
1628 goto error;
1629 }
1630 }
1631
1632 return 0;
1633error:
1634 return -1;
1635}
1636#endif /* Py_DEBUG */
1637
1638
1639/////////////////////////////////// Array Node
1640
1641
1642static PyHamtNode *
1643hamt_node_array_new(Py_ssize_t count)
1644{
1645 Py_ssize_t i;
1646
1647 PyHamtNode_Array *node = PyObject_GC_New(
1648 PyHamtNode_Array, &_PyHamt_ArrayNode_Type);
1649 if (node == NULL) {
1650 return NULL;
1651 }
1652
1653 for (i = 0; i < HAMT_ARRAY_NODE_SIZE; i++) {
1654 node->a_array[i] = NULL;
1655 }
1656
1657 node->a_count = count;
1658
1659 _PyObject_GC_TRACK(node);
1660 return (PyHamtNode *)node;
1661}
1662
1663static PyHamtNode_Array *
1664hamt_node_array_clone(PyHamtNode_Array *node)
1665{
1666 PyHamtNode_Array *clone;
1667 Py_ssize_t i;
1668
1669 VALIDATE_ARRAY_NODE(node)
1670
1671 /* Create a new Array node. */
1672 clone = (PyHamtNode_Array *)hamt_node_array_new(node->a_count);
1673 if (clone == NULL) {
1674 return NULL;
1675 }
1676
1677 /* Copy all elements from the current Array node to the new one. */
1678 for (i = 0; i < HAMT_ARRAY_NODE_SIZE; i++) {
1679 Py_XINCREF(node->a_array[i]);
1680 clone->a_array[i] = node->a_array[i];
1681 }
1682
1683 VALIDATE_ARRAY_NODE(clone)
1684 return clone;
1685}
1686
1687static PyHamtNode *
1688hamt_node_array_assoc(PyHamtNode_Array *self,
1689 uint32_t shift, int32_t hash,
1690 PyObject *key, PyObject *val, int* added_leaf)
1691{
1692 /* Set a new key to this level (currently a Collision node)
1693 of the tree.
1694
1695 Array nodes don't store values, they can only point to
1696 other nodes. They are simple arrays of 32 BaseNode pointers/
1697 */
1698
1699 uint32_t idx = hamt_mask(hash, shift);
1700 PyHamtNode *node = self->a_array[idx];
1701 PyHamtNode *child_node;
1702 PyHamtNode_Array *new_node;
1703 Py_ssize_t i;
1704
1705 if (node == NULL) {
1706 /* There's no child node for the given hash. Create a new
1707 Bitmap node for this key. */
1708
1709 PyHamtNode_Bitmap *empty = NULL;
1710
1711 /* Get an empty Bitmap node to work with. */
1712 empty = (PyHamtNode_Bitmap *)hamt_node_bitmap_new(0);
1713 if (empty == NULL) {
1714 return NULL;
1715 }
1716
1717 /* Set key/val to the newly created empty Bitmap, thus
1718 creating a new Bitmap node with our key/value pair. */
1719 child_node = hamt_node_bitmap_assoc(
1720 empty,
1721 shift + 5, hash, key, val, added_leaf);
1722 Py_DECREF(empty);
1723 if (child_node == NULL) {
1724 return NULL;
1725 }
1726
1727 /* Create a new Array node. */
1728 new_node = (PyHamtNode_Array *)hamt_node_array_new(self->a_count + 1);
1729 if (new_node == NULL) {
1730 Py_DECREF(child_node);
1731 return NULL;
1732 }
1733
1734 /* Copy all elements from the current Array node to the
1735 new one. */
1736 for (i = 0; i < HAMT_ARRAY_NODE_SIZE; i++) {
1737 Py_XINCREF(self->a_array[i]);
1738 new_node->a_array[i] = self->a_array[i];
1739 }
1740
1741 assert(new_node->a_array[idx] == NULL);
1742 new_node->a_array[idx] = child_node; /* borrow */
1743 VALIDATE_ARRAY_NODE(new_node)
1744 }
1745 else {
1746 /* There's a child node for the given hash.
1747 Set the key to it./ */
1748 child_node = hamt_node_assoc(
1749 node, shift + 5, hash, key, val, added_leaf);
Xiang Zhang3c7ac7e2018-03-08 13:59:46 +08001750 if (child_node == NULL) {
1751 return NULL;
1752 }
1753 else if (child_node == (PyHamtNode *)self) {
Yury Selivanovf23746a2018-01-22 19:11:18 -05001754 Py_DECREF(child_node);
1755 return (PyHamtNode *)self;
1756 }
1757
1758 new_node = hamt_node_array_clone(self);
1759 if (new_node == NULL) {
1760 Py_DECREF(child_node);
1761 return NULL;
1762 }
1763
1764 Py_SETREF(new_node->a_array[idx], child_node); /* borrow */
1765 VALIDATE_ARRAY_NODE(new_node)
1766 }
1767
1768 return (PyHamtNode *)new_node;
1769}
1770
1771static hamt_without_t
1772hamt_node_array_without(PyHamtNode_Array *self,
1773 uint32_t shift, int32_t hash,
1774 PyObject *key,
1775 PyHamtNode **new_node)
1776{
1777 uint32_t idx = hamt_mask(hash, shift);
1778 PyHamtNode *node = self->a_array[idx];
1779
1780 if (node == NULL) {
1781 return W_NOT_FOUND;
1782 }
1783
1784 PyHamtNode *sub_node = NULL;
1785 hamt_without_t res = hamt_node_without(
1786 (PyHamtNode *)node,
1787 shift + 5, hash, key, &sub_node);
1788
1789 switch (res) {
1790 case W_NOT_FOUND:
1791 case W_ERROR:
1792 assert(sub_node == NULL);
1793 return res;
1794
1795 case W_NEWNODE: {
1796 /* We need to replace a node at the `idx` index.
1797 Clone this node and replace.
1798 */
1799 assert(sub_node != NULL);
1800
1801 PyHamtNode_Array *clone = hamt_node_array_clone(self);
1802 if (clone == NULL) {
1803 Py_DECREF(sub_node);
1804 return W_ERROR;
1805 }
1806
1807 Py_SETREF(clone->a_array[idx], sub_node); /* borrow */
1808 *new_node = (PyHamtNode*)clone; /* borrow */
1809 return W_NEWNODE;
1810 }
1811
1812 case W_EMPTY: {
1813 assert(sub_node == NULL);
1814 /* We need to remove a node at the `idx` index.
1815 Calculate the size of the replacement Array node.
1816 */
1817 Py_ssize_t new_count = self->a_count - 1;
1818
1819 if (new_count == 0) {
1820 return W_EMPTY;
1821 }
1822
1823 if (new_count >= 16) {
1824 /* We convert Bitmap nodes to Array nodes, when a
1825 Bitmap node needs to store more than 15 key/value
1826 pairs. So we will create a new Array node if we
1827 the number of key/values after deletion is still
1828 greater than 15.
1829 */
1830
1831 PyHamtNode_Array *new = hamt_node_array_clone(self);
1832 if (new == NULL) {
1833 return W_ERROR;
1834 }
1835 new->a_count = new_count;
1836 Py_CLEAR(new->a_array[idx]);
1837
1838 *new_node = (PyHamtNode*)new; /* borrow */
1839 return W_NEWNODE;
1840 }
1841
1842 /* New Array node would have less than 16 key/value
1843 pairs. We need to create a replacement Bitmap node. */
1844
1845 Py_ssize_t bitmap_size = new_count * 2;
1846 uint32_t bitmap = 0;
1847
1848 PyHamtNode_Bitmap *new = (PyHamtNode_Bitmap *)
1849 hamt_node_bitmap_new(bitmap_size);
1850 if (new == NULL) {
1851 return W_ERROR;
1852 }
1853
1854 Py_ssize_t new_i = 0;
1855 for (uint32_t i = 0; i < HAMT_ARRAY_NODE_SIZE; i++) {
1856 if (i == idx) {
1857 /* Skip the node we are deleting. */
1858 continue;
1859 }
1860
1861 PyHamtNode *node = self->a_array[i];
1862 if (node == NULL) {
1863 /* Skip any missing nodes. */
1864 continue;
1865 }
1866
1867 bitmap |= 1 << i;
1868
1869 if (IS_BITMAP_NODE(node)) {
1870 PyHamtNode_Bitmap *child = (PyHamtNode_Bitmap *)node;
1871
1872 if (hamt_node_bitmap_count(child) == 1 &&
1873 child->b_array[0] != NULL)
1874 {
1875 /* node is a Bitmap with one key/value pair, just
1876 merge it into the new Bitmap node we're building.
1877
1878 Note that we don't inline Bitmap nodes that
1879 have a NULL key -- those nodes point to another
1880 tree level, and we cannot simply move tree levels
1881 up or down.
1882 */
1883 PyObject *key = child->b_array[0];
1884 PyObject *val = child->b_array[1];
1885
1886 Py_INCREF(key);
1887 new->b_array[new_i] = key;
1888 Py_INCREF(val);
1889 new->b_array[new_i + 1] = val;
1890 }
1891 else {
1892 new->b_array[new_i] = NULL;
1893 Py_INCREF(node);
1894 new->b_array[new_i + 1] = (PyObject*)node;
1895 }
1896 }
1897 else {
1898
1899#ifdef Py_DEBUG
1900 if (IS_COLLISION_NODE(node)) {
1901 Py_ssize_t child_count = hamt_node_collision_count(
1902 (PyHamtNode_Collision*)node);
1903 assert(child_count > 1);
1904 }
1905 else if (IS_ARRAY_NODE(node)) {
1906 assert(((PyHamtNode_Array*)node)->a_count >= 16);
1907 }
1908#endif
1909
1910 /* Just copy the node into our new Bitmap */
1911 new->b_array[new_i] = NULL;
1912 Py_INCREF(node);
1913 new->b_array[new_i + 1] = (PyObject*)node;
1914 }
1915
1916 new_i += 2;
1917 }
1918
1919 new->b_bitmap = bitmap;
1920 *new_node = (PyHamtNode*)new; /* borrow */
1921 return W_NEWNODE;
1922 }
1923
1924 default:
1925 Py_UNREACHABLE();
1926 }
1927}
1928
1929static hamt_find_t
1930hamt_node_array_find(PyHamtNode_Array *self,
1931 uint32_t shift, int32_t hash,
1932 PyObject *key, PyObject **val)
1933{
1934 /* Lookup `key` in the Array node `self`. Set the value
1935 for the found key to 'val'. */
1936
1937 uint32_t idx = hamt_mask(hash, shift);
1938 PyHamtNode *node;
1939
1940 node = self->a_array[idx];
1941 if (node == NULL) {
1942 return F_NOT_FOUND;
1943 }
1944
1945 /* Dispatch to the generic hamt_node_find */
1946 return hamt_node_find(node, shift + 5, hash, key, val);
1947}
1948
1949static int
1950hamt_node_array_traverse(PyHamtNode_Array *self,
1951 visitproc visit, void *arg)
1952{
1953 /* Array's tp_traverse */
1954
1955 Py_ssize_t i;
1956
1957 for (i = 0; i < HAMT_ARRAY_NODE_SIZE; i++) {
1958 Py_VISIT(self->a_array[i]);
1959 }
1960
1961 return 0;
1962}
1963
1964static void
1965hamt_node_array_dealloc(PyHamtNode_Array *self)
1966{
1967 /* Array's tp_dealloc */
1968
1969 Py_ssize_t i;
1970
1971 PyObject_GC_UnTrack(self);
Jeroen Demeyer351c6742019-05-10 19:21:11 +02001972 Py_TRASHCAN_BEGIN(self, hamt_node_array_dealloc)
Yury Selivanovf23746a2018-01-22 19:11:18 -05001973
1974 for (i = 0; i < HAMT_ARRAY_NODE_SIZE; i++) {
1975 Py_XDECREF(self->a_array[i]);
1976 }
1977
1978 Py_TYPE(self)->tp_free((PyObject *)self);
Jeroen Demeyer351c6742019-05-10 19:21:11 +02001979 Py_TRASHCAN_END
Yury Selivanovf23746a2018-01-22 19:11:18 -05001980}
1981
1982#ifdef Py_DEBUG
1983static int
1984hamt_node_array_dump(PyHamtNode_Array *node,
1985 _PyUnicodeWriter *writer, int level)
1986{
1987 /* Debug build: __dump__() method implementation for Array nodes. */
1988
1989 Py_ssize_t i;
1990
1991 if (_hamt_dump_ident(writer, level + 1)) {
1992 goto error;
1993 }
1994
1995 if (_hamt_dump_format(writer, "ArrayNode(id=%p):\n", node)) {
1996 goto error;
1997 }
1998
1999 for (i = 0; i < HAMT_ARRAY_NODE_SIZE; i++) {
2000 if (node->a_array[i] == NULL) {
2001 continue;
2002 }
2003
2004 if (_hamt_dump_ident(writer, level + 2)) {
2005 goto error;
2006 }
2007
Serhiy Storchakad53fe5f2019-03-13 22:59:55 +02002008 if (_hamt_dump_format(writer, "%zd::\n", i)) {
Yury Selivanovf23746a2018-01-22 19:11:18 -05002009 goto error;
2010 }
2011
2012 if (hamt_node_dump(node->a_array[i], writer, level + 1)) {
2013 goto error;
2014 }
2015
2016 if (_hamt_dump_format(writer, "\n")) {
2017 goto error;
2018 }
2019 }
2020
2021 return 0;
2022error:
2023 return -1;
2024}
2025#endif /* Py_DEBUG */
2026
2027
2028/////////////////////////////////// Node Dispatch
2029
2030
2031static PyHamtNode *
2032hamt_node_assoc(PyHamtNode *node,
2033 uint32_t shift, int32_t hash,
2034 PyObject *key, PyObject *val, int* added_leaf)
2035{
2036 /* Set key/value to the 'node' starting with the given shift/hash.
2037 Return a new node, or the same node if key/value already
2038 set.
2039
2040 added_leaf will be set to 1 if key/value wasn't in the
2041 tree before.
2042
2043 This method automatically dispatches to the suitable
2044 hamt_node_{nodetype}_assoc method.
2045 */
2046
2047 if (IS_BITMAP_NODE(node)) {
2048 return hamt_node_bitmap_assoc(
2049 (PyHamtNode_Bitmap *)node,
2050 shift, hash, key, val, added_leaf);
2051 }
2052 else if (IS_ARRAY_NODE(node)) {
2053 return hamt_node_array_assoc(
2054 (PyHamtNode_Array *)node,
2055 shift, hash, key, val, added_leaf);
2056 }
2057 else {
2058 assert(IS_COLLISION_NODE(node));
2059 return hamt_node_collision_assoc(
2060 (PyHamtNode_Collision *)node,
2061 shift, hash, key, val, added_leaf);
2062 }
2063}
2064
2065static hamt_without_t
2066hamt_node_without(PyHamtNode *node,
2067 uint32_t shift, int32_t hash,
2068 PyObject *key,
2069 PyHamtNode **new_node)
2070{
2071 if (IS_BITMAP_NODE(node)) {
2072 return hamt_node_bitmap_without(
2073 (PyHamtNode_Bitmap *)node,
2074 shift, hash, key,
2075 new_node);
2076 }
2077 else if (IS_ARRAY_NODE(node)) {
2078 return hamt_node_array_without(
2079 (PyHamtNode_Array *)node,
2080 shift, hash, key,
2081 new_node);
2082 }
2083 else {
2084 assert(IS_COLLISION_NODE(node));
2085 return hamt_node_collision_without(
2086 (PyHamtNode_Collision *)node,
2087 shift, hash, key,
2088 new_node);
2089 }
2090}
2091
2092static hamt_find_t
2093hamt_node_find(PyHamtNode *node,
2094 uint32_t shift, int32_t hash,
2095 PyObject *key, PyObject **val)
2096{
2097 /* Find the key in the node starting with the given shift/hash.
2098
2099 If a value is found, the result will be set to F_FOUND, and
2100 *val will point to the found value object.
2101
2102 If a value wasn't found, the result will be set to F_NOT_FOUND.
2103
2104 If an exception occurs during the call, the result will be F_ERROR.
2105
2106 This method automatically dispatches to the suitable
2107 hamt_node_{nodetype}_find method.
2108 */
2109
2110 if (IS_BITMAP_NODE(node)) {
2111 return hamt_node_bitmap_find(
2112 (PyHamtNode_Bitmap *)node,
2113 shift, hash, key, val);
2114
2115 }
2116 else if (IS_ARRAY_NODE(node)) {
2117 return hamt_node_array_find(
2118 (PyHamtNode_Array *)node,
2119 shift, hash, key, val);
2120 }
2121 else {
2122 assert(IS_COLLISION_NODE(node));
2123 return hamt_node_collision_find(
2124 (PyHamtNode_Collision *)node,
2125 shift, hash, key, val);
2126 }
2127}
2128
2129#ifdef Py_DEBUG
2130static int
2131hamt_node_dump(PyHamtNode *node,
2132 _PyUnicodeWriter *writer, int level)
2133{
2134 /* Debug build: __dump__() method implementation for a node.
2135
2136 This method automatically dispatches to the suitable
2137 hamt_node_{nodetype})_dump method.
2138 */
2139
2140 if (IS_BITMAP_NODE(node)) {
2141 return hamt_node_bitmap_dump(
2142 (PyHamtNode_Bitmap *)node, writer, level);
2143 }
2144 else if (IS_ARRAY_NODE(node)) {
2145 return hamt_node_array_dump(
2146 (PyHamtNode_Array *)node, writer, level);
2147 }
2148 else {
2149 assert(IS_COLLISION_NODE(node));
2150 return hamt_node_collision_dump(
2151 (PyHamtNode_Collision *)node, writer, level);
2152 }
2153}
2154#endif /* Py_DEBUG */
2155
2156
2157/////////////////////////////////// Iterators: Machinery
2158
2159
2160static hamt_iter_t
2161hamt_iterator_next(PyHamtIteratorState *iter, PyObject **key, PyObject **val);
2162
2163
2164static void
2165hamt_iterator_init(PyHamtIteratorState *iter, PyHamtNode *root)
2166{
2167 for (uint32_t i = 0; i < _Py_HAMT_MAX_TREE_DEPTH; i++) {
2168 iter->i_nodes[i] = NULL;
2169 iter->i_pos[i] = 0;
2170 }
2171
2172 iter->i_level = 0;
2173
2174 /* Note: we don't incref/decref nodes in i_nodes. */
2175 iter->i_nodes[0] = root;
2176}
2177
2178static hamt_iter_t
2179hamt_iterator_bitmap_next(PyHamtIteratorState *iter,
2180 PyObject **key, PyObject **val)
2181{
2182 int8_t level = iter->i_level;
2183
2184 PyHamtNode_Bitmap *node = (PyHamtNode_Bitmap *)(iter->i_nodes[level]);
2185 Py_ssize_t pos = iter->i_pos[level];
2186
2187 if (pos + 1 >= Py_SIZE(node)) {
2188#ifdef Py_DEBUG
2189 assert(iter->i_level >= 0);
2190 iter->i_nodes[iter->i_level] = NULL;
2191#endif
2192 iter->i_level--;
2193 return hamt_iterator_next(iter, key, val);
2194 }
2195
2196 if (node->b_array[pos] == NULL) {
2197 iter->i_pos[level] = pos + 2;
2198
2199 int8_t next_level = level + 1;
2200 assert(next_level < _Py_HAMT_MAX_TREE_DEPTH);
2201 iter->i_level = next_level;
2202 iter->i_pos[next_level] = 0;
2203 iter->i_nodes[next_level] = (PyHamtNode *)
2204 node->b_array[pos + 1];
2205
2206 return hamt_iterator_next(iter, key, val);
2207 }
2208
2209 *key = node->b_array[pos];
2210 *val = node->b_array[pos + 1];
2211 iter->i_pos[level] = pos + 2;
2212 return I_ITEM;
2213}
2214
2215static hamt_iter_t
2216hamt_iterator_collision_next(PyHamtIteratorState *iter,
2217 PyObject **key, PyObject **val)
2218{
2219 int8_t level = iter->i_level;
2220
2221 PyHamtNode_Collision *node = (PyHamtNode_Collision *)(iter->i_nodes[level]);
2222 Py_ssize_t pos = iter->i_pos[level];
2223
2224 if (pos + 1 >= Py_SIZE(node)) {
2225#ifdef Py_DEBUG
2226 assert(iter->i_level >= 0);
2227 iter->i_nodes[iter->i_level] = NULL;
2228#endif
2229 iter->i_level--;
2230 return hamt_iterator_next(iter, key, val);
2231 }
2232
2233 *key = node->c_array[pos];
2234 *val = node->c_array[pos + 1];
2235 iter->i_pos[level] = pos + 2;
2236 return I_ITEM;
2237}
2238
2239static hamt_iter_t
2240hamt_iterator_array_next(PyHamtIteratorState *iter,
2241 PyObject **key, PyObject **val)
2242{
2243 int8_t level = iter->i_level;
2244
2245 PyHamtNode_Array *node = (PyHamtNode_Array *)(iter->i_nodes[level]);
2246 Py_ssize_t pos = iter->i_pos[level];
2247
2248 if (pos >= HAMT_ARRAY_NODE_SIZE) {
2249#ifdef Py_DEBUG
2250 assert(iter->i_level >= 0);
2251 iter->i_nodes[iter->i_level] = NULL;
2252#endif
2253 iter->i_level--;
2254 return hamt_iterator_next(iter, key, val);
2255 }
2256
2257 for (Py_ssize_t i = pos; i < HAMT_ARRAY_NODE_SIZE; i++) {
2258 if (node->a_array[i] != NULL) {
2259 iter->i_pos[level] = i + 1;
2260
2261 int8_t next_level = level + 1;
2262 assert(next_level < _Py_HAMT_MAX_TREE_DEPTH);
2263 iter->i_pos[next_level] = 0;
2264 iter->i_nodes[next_level] = node->a_array[i];
2265 iter->i_level = next_level;
2266
2267 return hamt_iterator_next(iter, key, val);
2268 }
2269 }
2270
2271#ifdef Py_DEBUG
2272 assert(iter->i_level >= 0);
2273 iter->i_nodes[iter->i_level] = NULL;
2274#endif
2275
2276 iter->i_level--;
2277 return hamt_iterator_next(iter, key, val);
2278}
2279
2280static hamt_iter_t
2281hamt_iterator_next(PyHamtIteratorState *iter, PyObject **key, PyObject **val)
2282{
2283 if (iter->i_level < 0) {
2284 return I_END;
2285 }
2286
2287 assert(iter->i_level < _Py_HAMT_MAX_TREE_DEPTH);
2288
2289 PyHamtNode *current = iter->i_nodes[iter->i_level];
2290
2291 if (IS_BITMAP_NODE(current)) {
2292 return hamt_iterator_bitmap_next(iter, key, val);
2293 }
2294 else if (IS_ARRAY_NODE(current)) {
2295 return hamt_iterator_array_next(iter, key, val);
2296 }
2297 else {
2298 assert(IS_COLLISION_NODE(current));
2299 return hamt_iterator_collision_next(iter, key, val);
2300 }
2301}
2302
2303
2304/////////////////////////////////// HAMT high-level functions
2305
2306
2307PyHamtObject *
2308_PyHamt_Assoc(PyHamtObject *o, PyObject *key, PyObject *val)
2309{
2310 int32_t key_hash;
2311 int added_leaf = 0;
2312 PyHamtNode *new_root;
2313 PyHamtObject *new_o;
2314
2315 key_hash = hamt_hash(key);
2316 if (key_hash == -1) {
2317 return NULL;
2318 }
2319
2320 new_root = hamt_node_assoc(
2321 (PyHamtNode *)(o->h_root),
2322 0, key_hash, key, val, &added_leaf);
2323 if (new_root == NULL) {
2324 return NULL;
2325 }
2326
2327 if (new_root == o->h_root) {
2328 Py_DECREF(new_root);
2329 Py_INCREF(o);
2330 return o;
2331 }
2332
2333 new_o = hamt_alloc();
2334 if (new_o == NULL) {
2335 Py_DECREF(new_root);
2336 return NULL;
2337 }
2338
2339 new_o->h_root = new_root; /* borrow */
2340 new_o->h_count = added_leaf ? o->h_count + 1 : o->h_count;
2341
2342 return new_o;
2343}
2344
2345PyHamtObject *
2346_PyHamt_Without(PyHamtObject *o, PyObject *key)
2347{
2348 int32_t key_hash = hamt_hash(key);
2349 if (key_hash == -1) {
2350 return NULL;
2351 }
2352
Zackery Spytzd8c3e822018-07-06 02:50:38 -06002353 PyHamtNode *new_root = NULL;
Yury Selivanovf23746a2018-01-22 19:11:18 -05002354
2355 hamt_without_t res = hamt_node_without(
2356 (PyHamtNode *)(o->h_root),
2357 0, key_hash, key,
2358 &new_root);
2359
2360 switch (res) {
2361 case W_ERROR:
2362 return NULL;
2363 case W_EMPTY:
2364 return _PyHamt_New();
2365 case W_NOT_FOUND:
2366 Py_INCREF(o);
2367 return o;
2368 case W_NEWNODE: {
Yury Selivanov55e08392018-02-01 22:24:56 -05002369 assert(new_root != NULL);
2370
Yury Selivanovf23746a2018-01-22 19:11:18 -05002371 PyHamtObject *new_o = hamt_alloc();
2372 if (new_o == NULL) {
2373 Py_DECREF(new_root);
2374 return NULL;
2375 }
2376
2377 new_o->h_root = new_root; /* borrow */
2378 new_o->h_count = o->h_count - 1;
2379 assert(new_o->h_count >= 0);
2380 return new_o;
2381 }
2382 default:
2383 Py_UNREACHABLE();
2384 }
2385}
2386
2387static hamt_find_t
2388hamt_find(PyHamtObject *o, PyObject *key, PyObject **val)
2389{
2390 if (o->h_count == 0) {
2391 return F_NOT_FOUND;
2392 }
2393
2394 int32_t key_hash = hamt_hash(key);
2395 if (key_hash == -1) {
2396 return F_ERROR;
2397 }
2398
2399 return hamt_node_find(o->h_root, 0, key_hash, key, val);
2400}
2401
2402
2403int
2404_PyHamt_Find(PyHamtObject *o, PyObject *key, PyObject **val)
2405{
2406 hamt_find_t res = hamt_find(o, key, val);
2407 switch (res) {
2408 case F_ERROR:
2409 return -1;
2410 case F_NOT_FOUND:
2411 return 0;
2412 case F_FOUND:
2413 return 1;
2414 default:
2415 Py_UNREACHABLE();
2416 }
2417}
2418
2419
2420int
2421_PyHamt_Eq(PyHamtObject *v, PyHamtObject *w)
2422{
2423 if (v == w) {
2424 return 1;
2425 }
2426
2427 if (v->h_count != w->h_count) {
2428 return 0;
2429 }
2430
2431 PyHamtIteratorState iter;
2432 hamt_iter_t iter_res;
2433 hamt_find_t find_res;
2434 PyObject *v_key;
2435 PyObject *v_val;
2436 PyObject *w_val;
2437
2438 hamt_iterator_init(&iter, v->h_root);
2439
2440 do {
2441 iter_res = hamt_iterator_next(&iter, &v_key, &v_val);
2442 if (iter_res == I_ITEM) {
2443 find_res = hamt_find(w, v_key, &w_val);
2444 switch (find_res) {
2445 case F_ERROR:
2446 return -1;
2447
2448 case F_NOT_FOUND:
2449 return 0;
2450
2451 case F_FOUND: {
2452 int cmp = PyObject_RichCompareBool(v_val, w_val, Py_EQ);
2453 if (cmp < 0) {
2454 return -1;
2455 }
2456 if (cmp == 0) {
2457 return 0;
2458 }
2459 }
2460 }
2461 }
2462 } while (iter_res != I_END);
2463
2464 return 1;
2465}
2466
2467Py_ssize_t
2468_PyHamt_Len(PyHamtObject *o)
2469{
2470 return o->h_count;
2471}
2472
2473static PyHamtObject *
2474hamt_alloc(void)
2475{
2476 PyHamtObject *o;
2477 o = PyObject_GC_New(PyHamtObject, &_PyHamt_Type);
2478 if (o == NULL) {
2479 return NULL;
2480 }
Yury Selivanov378c53c2018-06-07 20:29:55 -04002481 o->h_count = 0;
2482 o->h_root = NULL;
Yury Selivanovf23746a2018-01-22 19:11:18 -05002483 o->h_weakreflist = NULL;
2484 PyObject_GC_Track(o);
2485 return o;
2486}
2487
2488PyHamtObject *
2489_PyHamt_New(void)
2490{
2491 if (_empty_hamt != NULL) {
2492 /* HAMT is an immutable object so we can easily cache an
2493 empty instance. */
2494 Py_INCREF(_empty_hamt);
2495 return _empty_hamt;
2496 }
2497
2498 PyHamtObject *o = hamt_alloc();
2499 if (o == NULL) {
2500 return NULL;
2501 }
2502
2503 o->h_root = hamt_node_bitmap_new(0);
2504 if (o->h_root == NULL) {
2505 Py_DECREF(o);
2506 return NULL;
2507 }
2508
2509 o->h_count = 0;
2510
2511 if (_empty_hamt == NULL) {
2512 Py_INCREF(o);
2513 _empty_hamt = o;
2514 }
2515
2516 return o;
2517}
2518
2519#ifdef Py_DEBUG
2520static PyObject *
2521hamt_dump(PyHamtObject *self)
2522{
2523 _PyUnicodeWriter writer;
2524
2525 _PyUnicodeWriter_Init(&writer);
2526
2527 if (_hamt_dump_format(&writer, "HAMT(len=%zd):\n", self->h_count)) {
2528 goto error;
2529 }
2530
2531 if (hamt_node_dump(self->h_root, &writer, 0)) {
2532 goto error;
2533 }
2534
2535 return _PyUnicodeWriter_Finish(&writer);
2536
2537error:
2538 _PyUnicodeWriter_Dealloc(&writer);
2539 return NULL;
2540}
2541#endif /* Py_DEBUG */
2542
2543
2544/////////////////////////////////// Iterators: Shared Iterator Implementation
2545
2546
2547static int
2548hamt_baseiter_tp_clear(PyHamtIterator *it)
2549{
2550 Py_CLEAR(it->hi_obj);
2551 return 0;
2552}
2553
2554static void
2555hamt_baseiter_tp_dealloc(PyHamtIterator *it)
2556{
2557 PyObject_GC_UnTrack(it);
2558 (void)hamt_baseiter_tp_clear(it);
2559 PyObject_GC_Del(it);
2560}
2561
2562static int
2563hamt_baseiter_tp_traverse(PyHamtIterator *it, visitproc visit, void *arg)
2564{
2565 Py_VISIT(it->hi_obj);
2566 return 0;
2567}
2568
2569static PyObject *
2570hamt_baseiter_tp_iternext(PyHamtIterator *it)
2571{
2572 PyObject *key;
2573 PyObject *val;
2574 hamt_iter_t res = hamt_iterator_next(&it->hi_iter, &key, &val);
2575
2576 switch (res) {
2577 case I_END:
2578 PyErr_SetNone(PyExc_StopIteration);
2579 return NULL;
2580
2581 case I_ITEM: {
2582 return (*(it->hi_yield))(key, val);
2583 }
2584
2585 default: {
2586 Py_UNREACHABLE();
2587 }
2588 }
2589}
2590
2591static Py_ssize_t
2592hamt_baseiter_tp_len(PyHamtIterator *it)
2593{
2594 return it->hi_obj->h_count;
2595}
2596
2597static PyMappingMethods PyHamtIterator_as_mapping = {
2598 (lenfunc)hamt_baseiter_tp_len,
2599};
2600
2601static PyObject *
2602hamt_baseiter_new(PyTypeObject *type, binaryfunc yield, PyHamtObject *o)
2603{
2604 PyHamtIterator *it = PyObject_GC_New(PyHamtIterator, type);
2605 if (it == NULL) {
2606 return NULL;
2607 }
2608
2609 Py_INCREF(o);
2610 it->hi_obj = o;
2611 it->hi_yield = yield;
2612
2613 hamt_iterator_init(&it->hi_iter, o->h_root);
2614
2615 return (PyObject*)it;
2616}
2617
2618#define ITERATOR_TYPE_SHARED_SLOTS \
2619 .tp_basicsize = sizeof(PyHamtIterator), \
2620 .tp_itemsize = 0, \
2621 .tp_as_mapping = &PyHamtIterator_as_mapping, \
2622 .tp_dealloc = (destructor)hamt_baseiter_tp_dealloc, \
2623 .tp_getattro = PyObject_GenericGetAttr, \
2624 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, \
2625 .tp_traverse = (traverseproc)hamt_baseiter_tp_traverse, \
2626 .tp_clear = (inquiry)hamt_baseiter_tp_clear, \
2627 .tp_iter = PyObject_SelfIter, \
2628 .tp_iternext = (iternextfunc)hamt_baseiter_tp_iternext,
2629
2630
2631/////////////////////////////////// _PyHamtItems_Type
2632
2633
2634PyTypeObject _PyHamtItems_Type = {
2635 PyVarObject_HEAD_INIT(NULL, 0)
2636 "items",
2637 ITERATOR_TYPE_SHARED_SLOTS
2638};
2639
2640static PyObject *
2641hamt_iter_yield_items(PyObject *key, PyObject *val)
2642{
2643 return PyTuple_Pack(2, key, val);
2644}
2645
2646PyObject *
2647_PyHamt_NewIterItems(PyHamtObject *o)
2648{
2649 return hamt_baseiter_new(
2650 &_PyHamtItems_Type, hamt_iter_yield_items, o);
2651}
2652
2653
2654/////////////////////////////////// _PyHamtKeys_Type
2655
2656
2657PyTypeObject _PyHamtKeys_Type = {
2658 PyVarObject_HEAD_INIT(NULL, 0)
2659 "keys",
2660 ITERATOR_TYPE_SHARED_SLOTS
2661};
2662
2663static PyObject *
2664hamt_iter_yield_keys(PyObject *key, PyObject *val)
2665{
2666 Py_INCREF(key);
2667 return key;
2668}
2669
2670PyObject *
2671_PyHamt_NewIterKeys(PyHamtObject *o)
2672{
2673 return hamt_baseiter_new(
2674 &_PyHamtKeys_Type, hamt_iter_yield_keys, o);
2675}
2676
2677
2678/////////////////////////////////// _PyHamtValues_Type
2679
2680
2681PyTypeObject _PyHamtValues_Type = {
2682 PyVarObject_HEAD_INIT(NULL, 0)
2683 "values",
2684 ITERATOR_TYPE_SHARED_SLOTS
2685};
2686
2687static PyObject *
2688hamt_iter_yield_values(PyObject *key, PyObject *val)
2689{
2690 Py_INCREF(val);
2691 return val;
2692}
2693
2694PyObject *
2695_PyHamt_NewIterValues(PyHamtObject *o)
2696{
2697 return hamt_baseiter_new(
2698 &_PyHamtValues_Type, hamt_iter_yield_values, o);
2699}
2700
2701
2702/////////////////////////////////// _PyHamt_Type
2703
2704
2705#ifdef Py_DEBUG
2706static PyObject *
2707hamt_dump(PyHamtObject *self);
2708#endif
2709
2710
2711static PyObject *
2712hamt_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
2713{
2714 return (PyObject*)_PyHamt_New();
2715}
2716
2717static int
2718hamt_tp_clear(PyHamtObject *self)
2719{
2720 Py_CLEAR(self->h_root);
2721 return 0;
2722}
2723
2724
2725static int
2726hamt_tp_traverse(PyHamtObject *self, visitproc visit, void *arg)
2727{
2728 Py_VISIT(self->h_root);
2729 return 0;
2730}
2731
2732static void
2733hamt_tp_dealloc(PyHamtObject *self)
2734{
2735 PyObject_GC_UnTrack(self);
2736 if (self->h_weakreflist != NULL) {
2737 PyObject_ClearWeakRefs((PyObject*)self);
2738 }
2739 (void)hamt_tp_clear(self);
2740 Py_TYPE(self)->tp_free(self);
2741}
2742
2743
2744static PyObject *
2745hamt_tp_richcompare(PyObject *v, PyObject *w, int op)
2746{
2747 if (!PyHamt_Check(v) || !PyHamt_Check(w) || (op != Py_EQ && op != Py_NE)) {
2748 Py_RETURN_NOTIMPLEMENTED;
2749 }
2750
2751 int res = _PyHamt_Eq((PyHamtObject *)v, (PyHamtObject *)w);
2752 if (res < 0) {
2753 return NULL;
2754 }
2755
2756 if (op == Py_NE) {
2757 res = !res;
2758 }
2759
2760 if (res) {
2761 Py_RETURN_TRUE;
2762 }
2763 else {
2764 Py_RETURN_FALSE;
2765 }
2766}
2767
2768static int
2769hamt_tp_contains(PyHamtObject *self, PyObject *key)
2770{
2771 PyObject *val;
2772 return _PyHamt_Find(self, key, &val);
2773}
2774
2775static PyObject *
2776hamt_tp_subscript(PyHamtObject *self, PyObject *key)
2777{
2778 PyObject *val;
2779 hamt_find_t res = hamt_find(self, key, &val);
2780 switch (res) {
2781 case F_ERROR:
2782 return NULL;
2783 case F_FOUND:
2784 Py_INCREF(val);
2785 return val;
2786 case F_NOT_FOUND:
2787 PyErr_SetObject(PyExc_KeyError, key);
2788 return NULL;
2789 default:
2790 Py_UNREACHABLE();
2791 }
2792}
2793
2794static Py_ssize_t
2795hamt_tp_len(PyHamtObject *self)
2796{
2797 return _PyHamt_Len(self);
2798}
2799
2800static PyObject *
2801hamt_tp_iter(PyHamtObject *self)
2802{
2803 return _PyHamt_NewIterKeys(self);
2804}
2805
2806static PyObject *
2807hamt_py_set(PyHamtObject *self, PyObject *args)
2808{
2809 PyObject *key;
2810 PyObject *val;
2811
2812 if (!PyArg_UnpackTuple(args, "set", 2, 2, &key, &val)) {
2813 return NULL;
2814 }
2815
2816 return (PyObject *)_PyHamt_Assoc(self, key, val);
2817}
2818
2819static PyObject *
2820hamt_py_get(PyHamtObject *self, PyObject *args)
2821{
2822 PyObject *key;
2823 PyObject *def = NULL;
2824
2825 if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &def)) {
2826 return NULL;
2827 }
2828
2829 PyObject *val = NULL;
2830 hamt_find_t res = hamt_find(self, key, &val);
2831 switch (res) {
2832 case F_ERROR:
2833 return NULL;
2834 case F_FOUND:
2835 Py_INCREF(val);
2836 return val;
2837 case F_NOT_FOUND:
2838 if (def == NULL) {
2839 Py_RETURN_NONE;
2840 }
2841 Py_INCREF(def);
2842 return def;
2843 default:
2844 Py_UNREACHABLE();
2845 }
2846}
2847
2848static PyObject *
2849hamt_py_delete(PyHamtObject *self, PyObject *key)
2850{
2851 return (PyObject *)_PyHamt_Without(self, key);
2852}
2853
2854static PyObject *
2855hamt_py_items(PyHamtObject *self, PyObject *args)
2856{
2857 return _PyHamt_NewIterItems(self);
2858}
2859
2860static PyObject *
2861hamt_py_values(PyHamtObject *self, PyObject *args)
2862{
2863 return _PyHamt_NewIterValues(self);
2864}
2865
2866static PyObject *
2867hamt_py_keys(PyHamtObject *self, PyObject *args)
2868{
2869 return _PyHamt_NewIterKeys(self);
2870}
2871
2872#ifdef Py_DEBUG
2873static PyObject *
2874hamt_py_dump(PyHamtObject *self, PyObject *args)
2875{
2876 return hamt_dump(self);
2877}
2878#endif
2879
2880
2881static PyMethodDef PyHamt_methods[] = {
2882 {"set", (PyCFunction)hamt_py_set, METH_VARARGS, NULL},
2883 {"get", (PyCFunction)hamt_py_get, METH_VARARGS, NULL},
2884 {"delete", (PyCFunction)hamt_py_delete, METH_O, NULL},
2885 {"items", (PyCFunction)hamt_py_items, METH_NOARGS, NULL},
2886 {"keys", (PyCFunction)hamt_py_keys, METH_NOARGS, NULL},
2887 {"values", (PyCFunction)hamt_py_values, METH_NOARGS, NULL},
2888#ifdef Py_DEBUG
2889 {"__dump__", (PyCFunction)hamt_py_dump, METH_NOARGS, NULL},
2890#endif
2891 {NULL, NULL}
2892};
2893
2894static PySequenceMethods PyHamt_as_sequence = {
2895 0, /* sq_length */
2896 0, /* sq_concat */
2897 0, /* sq_repeat */
2898 0, /* sq_item */
2899 0, /* sq_slice */
2900 0, /* sq_ass_item */
2901 0, /* sq_ass_slice */
2902 (objobjproc)hamt_tp_contains, /* sq_contains */
2903 0, /* sq_inplace_concat */
2904 0, /* sq_inplace_repeat */
2905};
2906
2907static PyMappingMethods PyHamt_as_mapping = {
2908 (lenfunc)hamt_tp_len, /* mp_length */
2909 (binaryfunc)hamt_tp_subscript, /* mp_subscript */
2910};
2911
2912PyTypeObject _PyHamt_Type = {
2913 PyVarObject_HEAD_INIT(&PyType_Type, 0)
2914 "hamt",
2915 sizeof(PyHamtObject),
2916 .tp_methods = PyHamt_methods,
2917 .tp_as_mapping = &PyHamt_as_mapping,
2918 .tp_as_sequence = &PyHamt_as_sequence,
2919 .tp_iter = (getiterfunc)hamt_tp_iter,
2920 .tp_dealloc = (destructor)hamt_tp_dealloc,
2921 .tp_getattro = PyObject_GenericGetAttr,
2922 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
2923 .tp_richcompare = hamt_tp_richcompare,
2924 .tp_traverse = (traverseproc)hamt_tp_traverse,
2925 .tp_clear = (inquiry)hamt_tp_clear,
2926 .tp_new = hamt_tp_new,
2927 .tp_weaklistoffset = offsetof(PyHamtObject, h_weakreflist),
2928 .tp_hash = PyObject_HashNotImplemented,
2929};
2930
2931
2932/////////////////////////////////// Tree Node Types
2933
2934
2935PyTypeObject _PyHamt_ArrayNode_Type = {
2936 PyVarObject_HEAD_INIT(&PyType_Type, 0)
2937 "hamt_array_node",
2938 sizeof(PyHamtNode_Array),
2939 0,
2940 .tp_dealloc = (destructor)hamt_node_array_dealloc,
2941 .tp_getattro = PyObject_GenericGetAttr,
2942 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
2943 .tp_traverse = (traverseproc)hamt_node_array_traverse,
2944 .tp_free = PyObject_GC_Del,
2945 .tp_hash = PyObject_HashNotImplemented,
2946};
2947
2948PyTypeObject _PyHamt_BitmapNode_Type = {
2949 PyVarObject_HEAD_INIT(&PyType_Type, 0)
2950 "hamt_bitmap_node",
2951 sizeof(PyHamtNode_Bitmap) - sizeof(PyObject *),
2952 sizeof(PyObject *),
2953 .tp_dealloc = (destructor)hamt_node_bitmap_dealloc,
2954 .tp_getattro = PyObject_GenericGetAttr,
2955 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
2956 .tp_traverse = (traverseproc)hamt_node_bitmap_traverse,
2957 .tp_free = PyObject_GC_Del,
2958 .tp_hash = PyObject_HashNotImplemented,
2959};
2960
2961PyTypeObject _PyHamt_CollisionNode_Type = {
2962 PyVarObject_HEAD_INIT(&PyType_Type, 0)
2963 "hamt_collision_node",
2964 sizeof(PyHamtNode_Collision) - sizeof(PyObject *),
2965 sizeof(PyObject *),
2966 .tp_dealloc = (destructor)hamt_node_collision_dealloc,
2967 .tp_getattro = PyObject_GenericGetAttr,
2968 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
2969 .tp_traverse = (traverseproc)hamt_node_collision_traverse,
2970 .tp_free = PyObject_GC_Del,
2971 .tp_hash = PyObject_HashNotImplemented,
2972};
2973
2974
2975int
2976_PyHamt_Init(void)
2977{
2978 if ((PyType_Ready(&_PyHamt_Type) < 0) ||
2979 (PyType_Ready(&_PyHamt_ArrayNode_Type) < 0) ||
2980 (PyType_Ready(&_PyHamt_BitmapNode_Type) < 0) ||
2981 (PyType_Ready(&_PyHamt_CollisionNode_Type) < 0) ||
2982 (PyType_Ready(&_PyHamtKeys_Type) < 0) ||
2983 (PyType_Ready(&_PyHamtValues_Type) < 0) ||
2984 (PyType_Ready(&_PyHamtItems_Type) < 0))
2985 {
2986 return 0;
2987 }
2988
2989 return 1;
2990}
2991
2992void
2993_PyHamt_Fini(void)
2994{
2995 Py_CLEAR(_empty_hamt);
2996 Py_CLEAR(_empty_bitmap_node);
2997}