Repository: numpy/numpy
File: numpy/_core/src/common/npy_hashtable.c (C)

Code before:
static inline Py_hash_t
identity_list_hash(PyObject *const *v, int len)
{
    Py_uhash_t acc = _NpyHASH_XXPRIME_5;
    for (int i = 0; i < len; i++) {
        /*
         * Lane is the single item hash, which for us is the rotated pointer.
         * Identical to the python type hash (pointers end with 0s normally).
         */
        size_t y = (size_t)v[i];
        Py_uhash_t lane = (y >> 4) | (y << (8 * SIZEOF_VOID_P - 4));
        acc += lane * _NpyHASH_XXPRIME_2;
        acc = _NpyHASH_XXROTATE(acc);
        acc *= _NpyHASH_XXPRIME_1;
    }
    return acc;
}
#undef _NpyHASH_XXPRIME_1
#undef _NpyHASH_XXPRIME_2
#undef _NpyHASH_XXPRIME_5
#undef _NpyHASH_XXROTATE


static inline PyObject **
find_item_buckets(struct buckets *buckets, int key_len, PyObject *const *key,
                PyObject **pvalue)
{
    Py_hash_t hash = identity_list_hash(key, key_len);
    npy_uintp perturb = (npy_uintp)hash;
    npy_intp mask = buckets->size - 1;
    npy_intp bucket = (npy_intp)hash & mask;

    while (1) {
        PyObject **item = &(buckets->array[bucket * (key_len + 1)]);
        PyObject *val = FT_ATOMIC_LOAD_PTR_ACQUIRE(item[0]);
        if (pvalue != NULL) {
            *pvalue = val;
        }
        if (val == NULL) {
            /* The item is not in the cache; return the empty bucket */
            return item;
        }
        if (memcmp(item+1, key, key_len * sizeof(PyObject *)) == 0) {
            /* This is a match, so return the item/bucket */
            return item;
        }
        /* Hash collision, perturb like Python (must happen rarely!) */
        perturb >>= 5;  /* Python uses the macro PERTURB_SHIFT == 5 */
        bucket = mask & (bucket * 5 + perturb + 1);
    }
}

Review comment (suggestion):
```suggestion
find_item_buckets(struct buckets *buckets, int key_len, PyObject *const *key,
                  PyObject **pvalue)
```

Code after:
static inline Py_hash_t
identity_list_hash(PyObject *const *v, int len)
{
    Py_uhash_t acc = _NpyHASH_XXPRIME_5;
    for (int i = 0; i < len; i++) {
        /*
         * Lane is the single item hash, which for us is the rotated pointer.
         * Identical to the python type hash (pointers end with 0s normally).
         */
        size_t y = (size_t)v[i];
        Py_uhash_t lane = (y >> 4) | (y << (8 * SIZEOF_VOID_P - 4));
        acc += lane * _NpyHASH_XXPRIME_2;
        acc = _NpyHASH_XXROTATE(acc);
        acc *= _NpyHASH_XXPRIME_1;
    }
    return acc;
}
#undef _NpyHASH_XXPRIME_1
#undef _NpyHASH_XXPRIME_2
#undef _NpyHASH_XXPRIME_5
#undef _NpyHASH_XXROTATE


static inline PyObject **
find_item_buckets(struct buckets *buckets, int key_len, PyObject *const *key,
                  PyObject **pvalue)
{
    Py_hash_t hash = identity_list_hash(key, key_len);
    npy_uintp perturb = (npy_uintp)hash;
    npy_intp mask = buckets->size - 1;
    npy_intp bucket = (npy_intp)hash & mask;

    while (1) {
        PyObject **item = &(buckets->array[bucket * (key_len + 1)]);
        PyObject *val = FT_ATOMIC_LOAD_PTR_ACQUIRE(item[0]);
        if (pvalue != NULL) {
            *pvalue = val;
        }
        if (val == NULL) {
            /* The item is not in the cache; return the empty bucket */
            return item;
        }
        if (memcmp(item+1, key, key_len * sizeof(PyObject *)) == 0) {
            /* This is a match, so return the item/bucket */
            return item;
        }
        /* Hash collision, perturb like Python (must happen rarely!) */
        perturb >>= 5;  /* Python uses the macro PERTURB_SHIFT == 5 */
        bucket = mask & (bucket * 5 + perturb + 1);
    }
}