Coverage Report

Created: 2026-03-23 06:45

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