Coverage Report

Created: 2025-07-11 06:59

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