Coverage Report

Created: 2025-07-04 06:49

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