LCOV - code coverage report
Current view: top level - Python - hamt.c (source / functions) Hit Total Coverage
Test: CPython lcov report Lines: 807 1040 77.6 %
Date: 2022-07-07 18:19:46 Functions: 71 82 86.6 %

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

Generated by: LCOV version 1.14