Coverage Report

Created: 2026-10-17 06:15

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