cpython/Python/hamt.c

#include "Python.h"
#include "pycore_bitutils.h"      // _Py_popcount32()
#include "pycore_hamt.h"
#include "pycore_initconfig.h"    // _PyStatus_OK()
#include "pycore_long.h"          // _PyLong_Format()
#include "pycore_object.h"        // _PyObject_GC_TRACK()

#include <stddef.h>               // offsetof()

/*
This file provides an implementation of an immutable mapping using the
Hash Array Mapped Trie (or HAMT) datastructure.

This design allows to have:

1. Efficient copy: immutable mappings can be copied by reference,
   making it an O(1) operation.

2. Efficient mutations: due to structural sharing, only a portion of
   the trie needs to be copied when the collection is mutated.  The
   cost of set/delete operations is O(log N).

3. Efficient lookups: O(log N).

(where N is number of key/value items in the immutable mapping.)


HAMT
====

The core idea of HAMT is that the shape of the trie is encoded into the
hashes of keys.

Say we want to store a K/V pair in our mapping.  First, we calculate the
hash of K, let's say it's 19830128, or in binary:

    0b1001011101001010101110000 = 19830128

Now let's partition this bit representation of the hash into blocks of
5 bits each:

    0b00_00000_10010_11101_00101_01011_10000 = 19830128
          (6)   (5)   (4)   (3)   (2)   (1)

Each block of 5 bits represents a number between 0 and 31.  So if we have
a tree that consists of nodes, each of which is an array of 32 pointers,
those 5-bit blocks will encode a position on a single tree level.

For example, storing the key K with hash 19830128, results in the following
tree structure:

                     (array of 32 pointers)
                     +---+ -- +----+----+----+ -- +----+
  root node          | 0 | .. | 15 | 16 | 17 | .. | 31 |   0b10000 = 16 (1)
  (level 1)          +---+ -- +----+----+----+ -- +----+
                                      |
                     +---+ -- +----+----+----+ -- +----+
  a 2nd level node   | 0 | .. | 10 | 11 | 12 | .. | 31 |   0b01011 = 11 (2)
                     +---+ -- +----+----+----+ -- +----+
                                      |
                     +---+ -- +----+----+----+ -- +----+
  a 3rd level node   | 0 | .. | 04 | 05 | 06 | .. | 31 |   0b00101 = 5  (3)
                     +---+ -- +----+----+----+ -- +----+
                                      |
                     +---+ -- +----+----+----+----+
  a 4th level node   | 0 | .. | 04 | 29 | 30 | 31 |        0b11101 = 29 (4)
                     +---+ -- +----+----+----+----+
                                      |
                     +---+ -- +----+----+----+ -- +----+
  a 5th level node   | 0 | .. | 17 | 18 | 19 | .. | 31 |   0b10010 = 18 (5)
                     +---+ -- +----+----+----+ -- +----+
                                      |
                       +--------------+
                       |
                     +---+ -- +----+----+----+ -- +----+
  a 6th level node   | 0 | .. | 15 | 16 | 17 | .. | 31 |   0b00000 = 0  (6)
                     +---+ -- +----+----+----+ -- +----+
                       |
                       V -- our value (or collision)

To rehash: for a K/V pair, the hash of K encodes where in the tree V will
be stored.

To optimize memory footprint and handle hash collisions, our implementation
uses three different types of nodes:

 * A Bitmap node;
 * An Array node;
 * A Collision node.

Because we implement an immutable dictionary, our nodes are also
immutable.  Therefore, when we need to modify a node, we copy it, and
do that modification to the copy.


Array Nodes
-----------

These nodes are very simple.  Essentially they are arrays of 32 pointers
we used to illustrate the high-level idea in the previous section.

We use Array nodes only when we need to store more than 16 pointers
in a single node.

Array nodes do not store key objects or value objects.  They are used
only as an indirection level - their pointers point to other nodes in
the tree.


Bitmap Node
-----------

Allocating a new 32-pointers array for every node of our tree would be
very expensive.  Unless we store millions of keys, most of tree nodes would
be very sparse.

When we have less than 16 elements in a node, we don't want to use the
Array node, that would mean that we waste a lot of memory.  Instead,
we can use bitmap compression and can have just as many pointers
as we need!

Bitmap nodes consist of two fields:

1. An array of pointers.  If a Bitmap node holds N elements, the
   array will be of N pointers.

2. A 32bit integer -- a bitmap field.  If an N-th bit is set in the
   bitmap, it means that the node has an N-th element.

For example, say we need to store a 3 elements sparse array:

   +---+  --  +---+  --  +----+  --  +----+
   | 0 |  ..  | 4 |  ..  | 11 |  ..  | 17 |
   +---+  --  +---+  --  +----+  --  +----+
                |          |           |
                o1         o2          o3

We allocate a three-pointer Bitmap node.  Its bitmap field will be
then set to:

   0b_00100_00010_00000_10000 == (1 << 17) | (1 << 11) | (1 << 4)

To check if our Bitmap node has an I-th element we can do:

   bitmap & (1 << I)


And here's a formula to calculate a position in our pointer array
which would correspond to an I-th element:

   popcount(bitmap & ((1 << I) - 1))


Let's break it down:

 * `popcount` is a function that returns a number of bits set to 1;

 * `((1 << I) - 1)` is a mask to filter the bitmask to contain bits
   set to the *right* of our bit.


So for our 17, 11, and 4 indexes:

 * bitmap & ((1 << 17) - 1) == 0b100000010000 => 2 bits are set => index is 2.

 * bitmap & ((1 << 11) - 1) == 0b10000 => 1 bit is set => index is 1.

 * bitmap & ((1 << 4) - 1) == 0b0 => 0 bits are set => index is 0.


To conclude: Bitmap nodes are just like Array nodes -- they can store
a number of pointers, but use bitmap compression to eliminate unused
pointers.


Bitmap nodes have two pointers for each item:

  +----+----+----+----+  --  +----+----+
  | k1 | v1 | k2 | v2 |  ..  | kN | vN |
  +----+----+----+----+  --  +----+----+

When kI == NULL, vI points to another tree level.

When kI != NULL, the actual key object is stored in kI, and its
value is stored in vI.


Collision Nodes
---------------

Collision nodes are simple arrays of pointers -- two pointers per
key/value.  When there's a hash collision, say for k1/v1 and k2/v2
we have `hash(k1)==hash(k2)`.  Then our collision node will be:

  +----+----+----+----+
  | k1 | v1 | k2 | v2 |
  +----+----+----+----+


Tree Structure
--------------

All nodes are PyObjects.

The `PyHamtObject` object has a pointer to the root node (h_root),
and has a length field (h_count).

High-level functions accept a PyHamtObject object and dispatch to
lower-level functions depending on what kind of node h_root points to.


Operations
==========

There are three fundamental operations on an immutable dictionary:

1. "o.assoc(k, v)" will return a new immutable dictionary, that will be
   a copy of "o", but with the "k/v" item set.

   Functions in this file:

        hamt_node_assoc, hamt_node_bitmap_assoc,
        hamt_node_array_assoc, hamt_node_collision_assoc

   `hamt_node_assoc` function accepts a node object, and calls
   other functions depending on its actual type.

2. "o.find(k)" will lookup key "k" in "o".

   Functions:

        hamt_node_find, hamt_node_bitmap_find,
        hamt_node_array_find, hamt_node_collision_find

3. "o.without(k)" will return a new immutable dictionary, that will be
   a copy of "o", buth without the "k" key.

   Functions:

        hamt_node_without, hamt_node_bitmap_without,
        hamt_node_array_without, hamt_node_collision_without


Further Reading
===============

1. http://blog.higher-order.net/2009/09/08/understanding-clojures-persistenthashmap-deftwice.html

2. http://blog.higher-order.net/2010/08/16/assoc-and-clojures-persistenthashmap-part-ii.html

3. Clojure's PersistentHashMap implementation:
   https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/PersistentHashMap.java


Debug
=====

The HAMT datatype is accessible for testing purposes under the
`_testcapi` module:

    >>> from _testcapi import hamt
    >>> h = hamt()
    >>> h2 = h.set('a', 2)
    >>> h3 = h2.set('b', 3)
    >>> list(h3)
    ['a', 'b']

When CPython is built in debug mode, a '__dump__()' method is available
to introspect the tree:

    >>> print(h3.__dump__())
    HAMT(len=2):
        BitmapNode(size=4 count=2 bitmap=0b110 id=0x10eb9d9e8):
            'a': 2
            'b': 3
*/


#define IS_ARRAY_NODE(node)
#define IS_BITMAP_NODE(node)
#define IS_COLLISION_NODE(node)


/* Return type for 'find' (lookup a key) functions.

   * F_ERROR - an error occurred;
   * F_NOT_FOUND - the key was not found;
   * F_FOUND - the key was found.
*/
hamt_find_t;


/* Return type for 'without' (delete a key) functions.

   * W_ERROR - an error occurred;
   * W_NOT_FOUND - the key was not found: there's nothing to delete;
   * W_EMPTY - the key was found: the node/tree would be empty
     if the key is deleted;
   * W_NEWNODE - the key was found: a new node/tree is returned
     without that key.
*/
hamt_without_t;


/* Low-level iterator protocol type.

   * I_ITEM - a new item has been yielded;
   * I_END - the whole tree was visited (similar to StopIteration).
*/
hamt_iter_t;


#define HAMT_ARRAY_NODE_SIZE


PyHamtNode_Array;


PyHamtNode_Collision;


static PyHamtObject *
hamt_alloc(void);

static PyHamtNode *
hamt_node_assoc(PyHamtNode *node,
                uint32_t shift, int32_t hash,
                PyObject *key, PyObject *val, int* added_leaf);

static hamt_without_t
hamt_node_without(PyHamtNode *node,
                  uint32_t shift, int32_t hash,
                  PyObject *key,
                  PyHamtNode **new_node);

static hamt_find_t
hamt_node_find(PyHamtNode *node,
               uint32_t shift, int32_t hash,
               PyObject *key, PyObject **val);

#ifdef Py_DEBUG
static int
hamt_node_dump(PyHamtNode *node,
               _PyUnicodeWriter *writer, int level);
#endif

static PyHamtNode *
hamt_node_array_new(Py_ssize_t);

static PyHamtNode *
hamt_node_collision_new(int32_t hash, Py_ssize_t size);

static inline Py_ssize_t
hamt_node_collision_count(PyHamtNode_Collision *node);


#ifdef Py_DEBUG
static void
_hamt_node_array_validate(void *obj_raw)
{
    PyObject *obj = _PyObject_CAST(obj_raw);
    assert(IS_ARRAY_NODE(obj));
    PyHamtNode_Array *node = (PyHamtNode_Array*)obj;
    Py_ssize_t i = 0, count = 0;
    for (; i < HAMT_ARRAY_NODE_SIZE; i++) {
        if (node->a_array[i] != NULL) {
            count++;
        }
    }
    assert(count == node->a_count);
}

#define VALIDATE_ARRAY_NODE
#else
#define VALIDATE_ARRAY_NODE(NODE)
#endif


/* Returns -1 on error */
static inline int32_t
hamt_hash(PyObject *o)
{}

static inline uint32_t
hamt_mask(int32_t hash, uint32_t shift)
{}

static inline uint32_t
hamt_bitpos(int32_t hash, uint32_t shift)
{}

static inline uint32_t
hamt_bitindex(uint32_t bitmap, uint32_t bit)
{}


/////////////////////////////////// Dump Helpers
#ifdef Py_DEBUG

static int
_hamt_dump_ident(_PyUnicodeWriter *writer, int level)
{
    /* Write `'    ' * level` to the `writer` */
    PyObject *str = NULL;
    PyObject *num = NULL;
    PyObject *res = NULL;
    int ret = -1;

    str = PyUnicode_FromString("    ");
    if (str == NULL) {
        goto error;
    }

    num = PyLong_FromLong((long)level);
    if (num == NULL) {
        goto error;
    }

    res = PyNumber_Multiply(str, num);
    if (res == NULL) {
        goto error;
    }

    ret = _PyUnicodeWriter_WriteStr(writer, res);

error:
    Py_XDECREF(res);
    Py_XDECREF(str);
    Py_XDECREF(num);
    return ret;
}

static int
_hamt_dump_format(_PyUnicodeWriter *writer, const char *format, ...)
{
    /* A convenient helper combining _PyUnicodeWriter_WriteStr and
       PyUnicode_FromFormatV.
    */
    PyObject* msg;
    int ret;

    va_list vargs;
    va_start(vargs, format);
    msg = PyUnicode_FromFormatV(format, vargs);
    va_end(vargs);

    if (msg == NULL) {
        return -1;
    }

    ret = _PyUnicodeWriter_WriteStr(writer, msg);
    Py_DECREF(msg);
    return ret;
}

#endif  /* Py_DEBUG */
/////////////////////////////////// Bitmap Node


static PyHamtNode *
hamt_node_bitmap_new(Py_ssize_t size)
{}

static inline Py_ssize_t
hamt_node_bitmap_count(PyHamtNode_Bitmap *node)
{}

static PyHamtNode_Bitmap *
hamt_node_bitmap_clone(PyHamtNode_Bitmap *node)
{}

static PyHamtNode_Bitmap *
hamt_node_bitmap_clone_without(PyHamtNode_Bitmap *o, uint32_t bit)
{}

static PyHamtNode *
hamt_node_new_bitmap_or_collision(uint32_t shift,
                                  PyObject *key1, PyObject *val1,
                                  int32_t key2_hash,
                                  PyObject *key2, PyObject *val2)
{}

static PyHamtNode *
hamt_node_bitmap_assoc(PyHamtNode_Bitmap *self,
                       uint32_t shift, int32_t hash,
                       PyObject *key, PyObject *val, int* added_leaf)
{}

static hamt_without_t
hamt_node_bitmap_without(PyHamtNode_Bitmap *self,
                         uint32_t shift, int32_t hash,
                         PyObject *key,
                         PyHamtNode **new_node)
{}

static hamt_find_t
hamt_node_bitmap_find(PyHamtNode_Bitmap *self,
                      uint32_t shift, int32_t hash,
                      PyObject *key, PyObject **val)
{}

static int
hamt_node_bitmap_traverse(PyHamtNode_Bitmap *self, visitproc visit, void *arg)
{}

static void
hamt_node_bitmap_dealloc(PyHamtNode_Bitmap *self)
{}

#ifdef Py_DEBUG
static int
hamt_node_bitmap_dump(PyHamtNode_Bitmap *node,
                      _PyUnicodeWriter *writer, int level)
{
    /* Debug build: __dump__() method implementation for Bitmap nodes. */

    Py_ssize_t i;
    PyObject *tmp1;
    PyObject *tmp2;

    if (_hamt_dump_ident(writer, level + 1)) {
        goto error;
    }

    if (_hamt_dump_format(writer, "BitmapNode(size=%zd count=%zd ",
                          Py_SIZE(node), Py_SIZE(node) / 2))
    {
        goto error;
    }

    tmp1 = PyLong_FromUnsignedLong(node->b_bitmap);
    if (tmp1 == NULL) {
        goto error;
    }
    tmp2 = _PyLong_Format(tmp1, 2);
    Py_DECREF(tmp1);
    if (tmp2 == NULL) {
        goto error;
    }
    if (_hamt_dump_format(writer, "bitmap=%S id=%p):\n", tmp2, node)) {
        Py_DECREF(tmp2);
        goto error;
    }
    Py_DECREF(tmp2);

    for (i = 0; i < Py_SIZE(node); i += 2) {
        PyObject *key_or_null = node->b_array[i];
        PyObject *val_or_node = node->b_array[i + 1];

        if (_hamt_dump_ident(writer, level + 2)) {
            goto error;
        }

        if (key_or_null == NULL) {
            if (_hamt_dump_format(writer, "NULL:\n")) {
                goto error;
            }

            if (hamt_node_dump((PyHamtNode *)val_or_node,
                               writer, level + 2))
            {
                goto error;
            }
        }
        else {
            if (_hamt_dump_format(writer, "%R: %R", key_or_null,
                                  val_or_node))
            {
                goto error;
            }
        }

        if (_hamt_dump_format(writer, "\n")) {
            goto error;
        }
    }

    return 0;
error:
    return -1;
}
#endif  /* Py_DEBUG */


/////////////////////////////////// Collision Node


static PyHamtNode *
hamt_node_collision_new(int32_t hash, Py_ssize_t size)
{}

static hamt_find_t
hamt_node_collision_find_index(PyHamtNode_Collision *self, PyObject *key,
                               Py_ssize_t *idx)
{}

static PyHamtNode *
hamt_node_collision_assoc(PyHamtNode_Collision *self,
                          uint32_t shift, int32_t hash,
                          PyObject *key, PyObject *val, int* added_leaf)
{}

static inline Py_ssize_t
hamt_node_collision_count(PyHamtNode_Collision *node)
{}

static hamt_without_t
hamt_node_collision_without(PyHamtNode_Collision *self,
                            uint32_t shift, int32_t hash,
                            PyObject *key,
                            PyHamtNode **new_node)
{}

static hamt_find_t
hamt_node_collision_find(PyHamtNode_Collision *self,
                         uint32_t shift, int32_t hash,
                         PyObject *key, PyObject **val)
{}


static int
hamt_node_collision_traverse(PyHamtNode_Collision *self,
                             visitproc visit, void *arg)
{}

static void
hamt_node_collision_dealloc(PyHamtNode_Collision *self)
{}

#ifdef Py_DEBUG
static int
hamt_node_collision_dump(PyHamtNode_Collision *node,
                         _PyUnicodeWriter *writer, int level)
{
    /* Debug build: __dump__() method implementation for Collision nodes. */

    Py_ssize_t i;

    if (_hamt_dump_ident(writer, level + 1)) {
        goto error;
    }

    if (_hamt_dump_format(writer, "CollisionNode(size=%zd id=%p):\n",
                          Py_SIZE(node), node))
    {
        goto error;
    }

    for (i = 0; i < Py_SIZE(node); i += 2) {
        PyObject *key = node->c_array[i];
        PyObject *val = node->c_array[i + 1];

        if (_hamt_dump_ident(writer, level + 2)) {
            goto error;
        }

        if (_hamt_dump_format(writer, "%R: %R\n", key, val)) {
            goto error;
        }
    }

    return 0;
error:
    return -1;
}
#endif  /* Py_DEBUG */


/////////////////////////////////// Array Node


static PyHamtNode *
hamt_node_array_new(Py_ssize_t count)
{}

static PyHamtNode_Array *
hamt_node_array_clone(PyHamtNode_Array *node)
{}

static PyHamtNode *
hamt_node_array_assoc(PyHamtNode_Array *self,
                      uint32_t shift, int32_t hash,
                      PyObject *key, PyObject *val, int* added_leaf)
{}

static hamt_without_t
hamt_node_array_without(PyHamtNode_Array *self,
                        uint32_t shift, int32_t hash,
                        PyObject *key,
                        PyHamtNode **new_node)
{}

static hamt_find_t
hamt_node_array_find(PyHamtNode_Array *self,
                     uint32_t shift, int32_t hash,
                     PyObject *key, PyObject **val)
{}

static int
hamt_node_array_traverse(PyHamtNode_Array *self,
                         visitproc visit, void *arg)
{}

static void
hamt_node_array_dealloc(PyHamtNode_Array *self)
{}

#ifdef Py_DEBUG
static int
hamt_node_array_dump(PyHamtNode_Array *node,
                     _PyUnicodeWriter *writer, int level)
{
    /* Debug build: __dump__() method implementation for Array nodes. */

    Py_ssize_t i;

    if (_hamt_dump_ident(writer, level + 1)) {
        goto error;
    }

    if (_hamt_dump_format(writer, "ArrayNode(id=%p):\n", node)) {
        goto error;
    }

    for (i = 0; i < HAMT_ARRAY_NODE_SIZE; i++) {
        if (node->a_array[i] == NULL) {
            continue;
        }

        if (_hamt_dump_ident(writer, level + 2)) {
            goto error;
        }

        if (_hamt_dump_format(writer, "%zd::\n", i)) {
            goto error;
        }

        if (hamt_node_dump(node->a_array[i], writer, level + 1)) {
            goto error;
        }

        if (_hamt_dump_format(writer, "\n")) {
            goto error;
        }
    }

    return 0;
error:
    return -1;
}
#endif  /* Py_DEBUG */


/////////////////////////////////// Node Dispatch


static PyHamtNode *
hamt_node_assoc(PyHamtNode *node,
                uint32_t shift, int32_t hash,
                PyObject *key, PyObject *val, int* added_leaf)
{}

static hamt_without_t
hamt_node_without(PyHamtNode *node,
                  uint32_t shift, int32_t hash,
                  PyObject *key,
                  PyHamtNode **new_node)
{}

static hamt_find_t
hamt_node_find(PyHamtNode *node,
               uint32_t shift, int32_t hash,
               PyObject *key, PyObject **val)
{}

#ifdef Py_DEBUG
static int
hamt_node_dump(PyHamtNode *node,
               _PyUnicodeWriter *writer, int level)
{
    /* Debug build: __dump__() method implementation for a node.

       This method automatically dispatches to the suitable
       hamt_node_{nodetype})_dump method.
    */

    if (IS_BITMAP_NODE(node)) {
        return hamt_node_bitmap_dump(
            (PyHamtNode_Bitmap *)node, writer, level);
    }
    else if (IS_ARRAY_NODE(node)) {
        return hamt_node_array_dump(
            (PyHamtNode_Array *)node, writer, level);
    }
    else {
        assert(IS_COLLISION_NODE(node));
        return hamt_node_collision_dump(
            (PyHamtNode_Collision *)node, writer, level);
    }
}
#endif  /* Py_DEBUG */


/////////////////////////////////// Iterators: Machinery


static hamt_iter_t
hamt_iterator_next(PyHamtIteratorState *iter, PyObject **key, PyObject **val);


static void
hamt_iterator_init(PyHamtIteratorState *iter, PyHamtNode *root)
{}

static hamt_iter_t
hamt_iterator_bitmap_next(PyHamtIteratorState *iter,
                          PyObject **key, PyObject **val)
{}

static hamt_iter_t
hamt_iterator_collision_next(PyHamtIteratorState *iter,
                             PyObject **key, PyObject **val)
{}

static hamt_iter_t
hamt_iterator_array_next(PyHamtIteratorState *iter,
                         PyObject **key, PyObject **val)
{}

static hamt_iter_t
hamt_iterator_next(PyHamtIteratorState *iter, PyObject **key, PyObject **val)
{}


/////////////////////////////////// HAMT high-level functions


PyHamtObject *
_PyHamt_Assoc(PyHamtObject *o, PyObject *key, PyObject *val)
{}

PyHamtObject *
_PyHamt_Without(PyHamtObject *o, PyObject *key)
{}

static hamt_find_t
hamt_find(PyHamtObject *o, PyObject *key, PyObject **val)
{}


int
_PyHamt_Find(PyHamtObject *o, PyObject *key, PyObject **val)
{}


int
_PyHamt_Eq(PyHamtObject *v, PyHamtObject *w)
{}

Py_ssize_t
_PyHamt_Len(PyHamtObject *o)
{}

static PyHamtObject *
hamt_alloc(void)
{}

#define _empty_hamt

PyHamtObject *
_PyHamt_New(void)
{}

#ifdef Py_DEBUG
static PyObject *
hamt_dump(PyHamtObject *self)
{
    _PyUnicodeWriter writer;

    _PyUnicodeWriter_Init(&writer);

    if (_hamt_dump_format(&writer, "HAMT(len=%zd):\n", self->h_count)) {
        goto error;
    }

    if (hamt_node_dump(self->h_root, &writer, 0)) {
        goto error;
    }

    return _PyUnicodeWriter_Finish(&writer);

error:
    _PyUnicodeWriter_Dealloc(&writer);
    return NULL;
}
#endif  /* Py_DEBUG */


/////////////////////////////////// Iterators: Shared Iterator Implementation


static int
hamt_baseiter_tp_clear(PyHamtIterator *it)
{}

static void
hamt_baseiter_tp_dealloc(PyHamtIterator *it)
{}

static int
hamt_baseiter_tp_traverse(PyHamtIterator *it, visitproc visit, void *arg)
{}

static PyObject *
hamt_baseiter_tp_iternext(PyHamtIterator *it)
{}

static Py_ssize_t
hamt_baseiter_tp_len(PyHamtIterator *it)
{}

static PyMappingMethods PyHamtIterator_as_mapping =;

static PyObject *
hamt_baseiter_new(PyTypeObject *type, binaryfunc yield, PyHamtObject *o)
{}

#define ITERATOR_TYPE_SHARED_SLOTS


/////////////////////////////////// _PyHamtItems_Type


PyTypeObject _PyHamtItems_Type =;

static PyObject *
hamt_iter_yield_items(PyObject *key, PyObject *val)
{}

PyObject *
_PyHamt_NewIterItems(PyHamtObject *o)
{}


/////////////////////////////////// _PyHamtKeys_Type


PyTypeObject _PyHamtKeys_Type =;

static PyObject *
hamt_iter_yield_keys(PyObject *key, PyObject *val)
{}

PyObject *
_PyHamt_NewIterKeys(PyHamtObject *o)
{}


/////////////////////////////////// _PyHamtValues_Type


PyTypeObject _PyHamtValues_Type =;

static PyObject *
hamt_iter_yield_values(PyObject *key, PyObject *val)
{}

PyObject *
_PyHamt_NewIterValues(PyHamtObject *o)
{}


/////////////////////////////////// _PyHamt_Type


#ifdef Py_DEBUG
static PyObject *
hamt_dump(PyHamtObject *self);
#endif


static PyObject *
hamt_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{}

static int
hamt_tp_clear(PyHamtObject *self)
{}


static int
hamt_tp_traverse(PyHamtObject *self, visitproc visit, void *arg)
{}

static void
hamt_tp_dealloc(PyHamtObject *self)
{}


static PyObject *
hamt_tp_richcompare(PyObject *v, PyObject *w, int op)
{}

static int
hamt_tp_contains(PyHamtObject *self, PyObject *key)
{}

static PyObject *
hamt_tp_subscript(PyHamtObject *self, PyObject *key)
{}

static Py_ssize_t
hamt_tp_len(PyHamtObject *self)
{}

static PyObject *
hamt_tp_iter(PyHamtObject *self)
{}

static PyObject *
hamt_py_set(PyHamtObject *self, PyObject *args)
{}

static PyObject *
hamt_py_get(PyHamtObject *self, PyObject *args)
{}

static PyObject *
hamt_py_delete(PyHamtObject *self, PyObject *key)
{}

static PyObject *
hamt_py_items(PyHamtObject *self, PyObject *args)
{}

static PyObject *
hamt_py_values(PyHamtObject *self, PyObject *args)
{}

static PyObject *
hamt_py_keys(PyHamtObject *self, PyObject *Py_UNUSED(args))
{}

#ifdef Py_DEBUG
static PyObject *
hamt_py_dump(PyHamtObject *self, PyObject *Py_UNUSED(args))
{
    return hamt_dump(self);
}
#endif


static PyMethodDef PyHamt_methods[] =;

static PySequenceMethods PyHamt_as_sequence =;

static PyMappingMethods PyHamt_as_mapping =;

PyTypeObject _PyHamt_Type =;


/////////////////////////////////// Tree Node Types


PyTypeObject _PyHamt_ArrayNode_Type =;

PyTypeObject _PyHamt_BitmapNode_Type =;

PyTypeObject _PyHamt_CollisionNode_Type =;