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