Coverage Report

Created: 2026-05-30 06:18

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