Welcome to little lamb

Code » limb » master » tree

[master] / src / liblimb / hmap.h / grow.c

/* This file is part of limb                           https://lila.oss/limb
 * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
/* SPDX-License-Identifier: GPL-2.0-only */
#include "hmap.h"

static void
relocate(u32 idx, u32 min, u32 max, u32 *left, u32 relocated[], hmap *hmap)
{
    /* keep in sync w/ item in hmap.h */
    struct fullitem {
        u32 key;
        u8 data[DLEN(hmap)];
    } * it;

    it = ITEM(idx, hmap);

    u32 n = idx / 32;
    u32 b = 1 << (idx - n);
    if ((relocated[n] & b) || !it->key)
        return;

    struct fullitem itmp, *new;

    /* remove item from current spot */
    itmp = *it;
    memset(it, 0, sizeof(*it));

    /* and look for its natural spot, or first available collision spot
     * if needed */
    u32 i, j;
    for (i = itmp.key, j = 1; ; i += j++) {
        /* get item's wanted spot */
        new = ITEM(i & hmap->sa.a, hmap);

        /* put it back if it's where it was */
        if (new == it)
            break;

        /* make sure its wanted spot has been relocated if needed */
        relocate(i & hmap->sa.a, min, max, left, relocated, hmap);

        /* settle there if it's now available */
        if (!new->key)
            break;
    }
    /* put item in its (new) spot */
    *new = itmp;
    n = (i & hmap->sa.a) / 32;
    b = 1 << ((i & hmap->sa.a) - n);
    relocated[n] |= b;
    /* decrease left iif it was within [min,max] */
    if (idx >= min && idx <= max)
        --*left;
}

int
grow(size_t n, hmap *hmap)
{
    u32 oldsize, newsize;

    if (n > MAXSIZE)
        n = MAXSIZE;

    for (newsize = MINSIZE; newsize < n; newsize <<= 1)
        ;

    /* if sa.a was turned into the mask.. */
    if (hmap->sa.a) {
        /* ..make it the (currently) allocated size again */
        ++hmap->sa.a;
        hmap->sa.a *= hmap->ilen;
    }
    oldsize = hmap->sa.a / hmap->ilen;

    if (!stralloc_ready_tuned(&hmap->sa, newsize * hmap->ilen, 0, 0, 1)) {
        hmap->sa.a /= hmap->ilen;
        --hmap->sa.a;
        return 0;
    }

    /* empty the new items */
    memset(hmap->sa.s + oldsize * hmap->ilen, 0, (newsize - oldsize) * hmap->ilen);

    /* make sa.a the new mask */
    hmap->sa.a /= hmap->ilen;
    --hmap->sa.a;

    /* we'll (try to) relocate all items to their (new) "natural" spots, or
     * a new collision/alternate spot if one got freed */
    u32 left = hmap->sa.len;
    u32 relocated[((hmap->sa.a + 1) / 32) + 1];
    memset(relocated, 0, sizeof(relocated));
    for (u32 i = 0; left; ++i) {
        struct item *it = ITEM(i, hmap);
        if (it->key)
            relocate(i, i, oldsize, &left, relocated, hmap);
    }

    return 1;
}