1a74702c6SGeorge Wang/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
250aadb33SDmitri Tikhonov/*
350aadb33SDmitri Tikhonov * lsquic_malo.c -- malo allocator implementation.
450aadb33SDmitri Tikhonov *
550aadb33SDmitri Tikhonov * The malo allocator is a pool of objects of fixed size.  It tries to
650aadb33SDmitri Tikhonov * allocate and deallocate objects as fast as possible.  To do so, it
750aadb33SDmitri Tikhonov * does the following:
850aadb33SDmitri Tikhonov *
950aadb33SDmitri Tikhonov *  1. Allocations occur 4 KB at a time.
105392f7a3SLiteSpeed Tech *  2. No division or multiplication operations are performed for
115392f7a3SLiteSpeed Tech *     appropriately sized objects.  (More on this below.)
1250aadb33SDmitri Tikhonov *
1350aadb33SDmitri Tikhonov * (In recent testing, malo was about 2.7 times faster than malloc for
1450aadb33SDmitri Tikhonov * 64-byte objects.)
1550aadb33SDmitri Tikhonov *
1650aadb33SDmitri Tikhonov * Besides speed, two other important characteristics distinguish it
1750aadb33SDmitri Tikhonov * from other pool allocators:
1850aadb33SDmitri Tikhonov *
1950aadb33SDmitri Tikhonov *  1. To free (put) an object, one does not need a pointer to the malo
2050aadb33SDmitri Tikhonov *     object.  This makes this allocator easy to use.
2150aadb33SDmitri Tikhonov *  2. A built-in iterator is provided to iterate over all allocated
225392f7a3SLiteSpeed Tech *     objects (with ability safely to release objects while iterator
2350aadb33SDmitri Tikhonov *     is active).  This may be useful in some circumstances.
2450aadb33SDmitri Tikhonov *
2550aadb33SDmitri Tikhonov * To gain all these advantages, there are trade-offs:
2650aadb33SDmitri Tikhonov *
2750aadb33SDmitri Tikhonov *  1. There are two memory penalties:
285392f7a3SLiteSpeed Tech *      a. Per object overhead.  If an object is at least ROUNDUP_THRESH in
295392f7a3SLiteSpeed Tech *         size as the next power of two, the allocator uses that power of
305392f7a3SLiteSpeed Tech *         two value as the object size.  This is done to avoid using
315392f7a3SLiteSpeed Tech *         division and multiplication.  For example, a 104-byte object
325392f7a3SLiteSpeed Tech *         will have a 24-byte overhead.
3350aadb33SDmitri Tikhonov *      b. Per page overhead.  Page links occupy some bytes in the
3450aadb33SDmitri Tikhonov *         page.  To keep things fast, at least one slot per page is
3550aadb33SDmitri Tikhonov *         always occupied, independent of object size.  Thus, for a
3650aadb33SDmitri Tikhonov *         1 KB object size, 25% of the page is used for the page
3750aadb33SDmitri Tikhonov *         header.
3850aadb33SDmitri Tikhonov *  2. 4 KB pages are not freed until the malo allocator is destroyed.
3950aadb33SDmitri Tikhonov *     This is something to keep in mind.
4050aadb33SDmitri Tikhonov *
4150aadb33SDmitri Tikhonov * P.S. In Russian, "malo" (��������) means "little" or "few".  Thus, the
4250aadb33SDmitri Tikhonov *      malo allocator aims to perform its job in as few CPU cycles as
4350aadb33SDmitri Tikhonov *      possible.
4450aadb33SDmitri Tikhonov */
4550aadb33SDmitri Tikhonov
4650aadb33SDmitri Tikhonov#include <assert.h>
4750aadb33SDmitri Tikhonov#include <errno.h>
4850aadb33SDmitri Tikhonov#include <stdint.h>
4950aadb33SDmitri Tikhonov#include <stdlib.h>
5050aadb33SDmitri Tikhonov#include <sys/queue.h>
51461e84d8SAmol Deshpande#ifdef WIN32
52461e84d8SAmol Deshpande#include <vc_compat.h>
532d296031SDmitri Tikhonov#include <intrin.h>
54461e84d8SAmol Deshpande#endif
5550aadb33SDmitri Tikhonov
5650aadb33SDmitri Tikhonov#include "fiu-local.h"
5750aadb33SDmitri Tikhonov#include "lsquic_malo.h"
5850aadb33SDmitri Tikhonov
595392f7a3SLiteSpeed Tech#ifndef LSQUIC_USE_POOLS
605392f7a3SLiteSpeed Tech#define LSQUIC_USE_POOLS 1
615392f7a3SLiteSpeed Tech#endif
625392f7a3SLiteSpeed Tech
6350aadb33SDmitri Tikhonov/* 64 slots in a 4KB page means that the smallest object is 64 bytes.
6450aadb33SDmitri Tikhonov * The largest object is 2KB.
6550aadb33SDmitri Tikhonov */
6650aadb33SDmitri Tikhonov#define MALO_MIN_NBITS 6
6750aadb33SDmitri Tikhonov#define MALO_MAX_NBITS 11
6850aadb33SDmitri Tikhonov
695392f7a3SLiteSpeed Tech#define ROUNDUP_THRESH 0.75f
705392f7a3SLiteSpeed Tech
7150aadb33SDmitri Tikhonov/* A "free page" is a page with free slots available.
7250aadb33SDmitri Tikhonov */
7350aadb33SDmitri Tikhonov
745392f7a3SLiteSpeed Tech#if LSQUIC_USE_POOLS
7550aadb33SDmitri Tikhonovstatic unsigned find_free_slot (uint64_t slots);
7650aadb33SDmitri Tikhonovstatic unsigned size_in_bits (size_t sz);
775392f7a3SLiteSpeed Tech#endif
7850aadb33SDmitri Tikhonov
7950aadb33SDmitri Tikhonovstruct malo_page {
8050aadb33SDmitri Tikhonov    SLIST_ENTRY(malo_page)  next_page;
8150aadb33SDmitri Tikhonov    LIST_ENTRY(malo_page)   next_free_page;
8250aadb33SDmitri Tikhonov    struct malo            *malo;
8350aadb33SDmitri Tikhonov    uint64_t                slots,
8450aadb33SDmitri Tikhonov                            full_slot_mask;
855392f7a3SLiteSpeed Tech    unsigned                nbits;  /* If pow is zero, stores object size */
8650aadb33SDmitri Tikhonov    unsigned                initial_slot;
875392f7a3SLiteSpeed Tech    int                     pow;    /* True if object is power of 2 */
8850aadb33SDmitri Tikhonov};
8950aadb33SDmitri Tikhonov
9050aadb33SDmitri Tikhonovtypedef char malo_header_fits_in_one_slot
915392f7a3SLiteSpeed Tech    [(sizeof(struct malo_page) > (1 << MALO_MIN_NBITS)) ? -1 : 1];
925392f7a3SLiteSpeed Tech
935392f7a3SLiteSpeed Tech#if !LSQUIC_USE_POOLS
945392f7a3SLiteSpeed Techstruct nopool_elem
955392f7a3SLiteSpeed Tech{
965392f7a3SLiteSpeed Tech    TAILQ_ENTRY(nopool_elem)    next;
975392f7a3SLiteSpeed Tech    struct malo                *malo;
985392f7a3SLiteSpeed Tech    unsigned char               data[0];
995392f7a3SLiteSpeed Tech};
1005392f7a3SLiteSpeed Tech#endif
10150aadb33SDmitri Tikhonov
10250aadb33SDmitri Tikhonovstruct malo {
1035392f7a3SLiteSpeed Tech#if LSQUIC_USE_POOLS
10450aadb33SDmitri Tikhonov    struct malo_page        page_header;
10550aadb33SDmitri Tikhonov    SLIST_HEAD(, malo_page) all_pages;
10650aadb33SDmitri Tikhonov    LIST_HEAD(, malo_page)  free_pages;
10750aadb33SDmitri Tikhonov    struct {
10850aadb33SDmitri Tikhonov        struct malo_page   *cur_page;
10950aadb33SDmitri Tikhonov        unsigned            next_slot;
11050aadb33SDmitri Tikhonov    }                       iter;
1115392f7a3SLiteSpeed Tech#else
1125392f7a3SLiteSpeed Tech    /* List of all elements: used by the iterator */
1135392f7a3SLiteSpeed Tech    TAILQ_HEAD(, nopool_elem)   elems;
1145392f7a3SLiteSpeed Tech    size_t                      obj_size;
1155392f7a3SLiteSpeed Tech    struct nopool_elem         *next_iter_elem;
1165392f7a3SLiteSpeed Tech#endif
11750aadb33SDmitri Tikhonov};
11850aadb33SDmitri Tikhonov
11950aadb33SDmitri Tikhonovstruct malo *
12050aadb33SDmitri Tikhonovlsquic_malo_create (size_t obj_size)
12150aadb33SDmitri Tikhonov{
1225392f7a3SLiteSpeed Tech#if LSQUIC_USE_POOLS
1235392f7a3SLiteSpeed Tech    int pow, n_slots;
1245392f7a3SLiteSpeed Tech    unsigned nbits;
1255392f7a3SLiteSpeed Tech
1265392f7a3SLiteSpeed Tech    obj_size = (obj_size + 7) & -8;
1275392f7a3SLiteSpeed Tech    nbits = size_in_bits(obj_size);
12850aadb33SDmitri Tikhonov    if (nbits < MALO_MIN_NBITS)
12950aadb33SDmitri Tikhonov        nbits = MALO_MIN_NBITS;
13050aadb33SDmitri Tikhonov    else if (nbits > MALO_MAX_NBITS)
13150aadb33SDmitri Tikhonov    {
13250aadb33SDmitri Tikhonov        errno = EOVERFLOW;
13350aadb33SDmitri Tikhonov        return NULL;
13450aadb33SDmitri Tikhonov    }
13550aadb33SDmitri Tikhonov
1365392f7a3SLiteSpeed Tech    pow = obj_size <= (1 << MALO_MIN_NBITS)
1375392f7a3SLiteSpeed Tech          || (float) obj_size / (1 << nbits) > ROUNDUP_THRESH;
1385392f7a3SLiteSpeed Tech
13950aadb33SDmitri Tikhonov    struct malo *malo;
14050aadb33SDmitri Tikhonov    if (0 != posix_memalign((void **) &malo, 0x1000, 0x1000))
14150aadb33SDmitri Tikhonov        return NULL;
14250aadb33SDmitri Tikhonov
14350aadb33SDmitri Tikhonov    SLIST_INIT(&malo->all_pages);
14450aadb33SDmitri Tikhonov    LIST_INIT(&malo->free_pages);
14550aadb33SDmitri Tikhonov    malo->iter.cur_page = &malo->page_header;
14650aadb33SDmitri Tikhonov    malo->iter.next_slot = 0;
14750aadb33SDmitri Tikhonov
1485392f7a3SLiteSpeed Tech    if (pow)
1495392f7a3SLiteSpeed Tech        n_slots =   sizeof(*malo) / (1 << nbits)
15050aadb33SDmitri Tikhonov                + ((sizeof(*malo) % (1 << nbits)) > 0);
1515392f7a3SLiteSpeed Tech    else
1525392f7a3SLiteSpeed Tech        n_slots =   sizeof(*malo) / obj_size
1535392f7a3SLiteSpeed Tech                + ((sizeof(*malo) % obj_size) > 0);
15450aadb33SDmitri Tikhonov
15550aadb33SDmitri Tikhonov    struct malo_page *const page = &malo->page_header;
15650aadb33SDmitri Tikhonov    SLIST_INSERT_HEAD(&malo->all_pages, page, next_page);
15750aadb33SDmitri Tikhonov    LIST_INSERT_HEAD(&malo->free_pages, page, next_free_page);
15850aadb33SDmitri Tikhonov    page->malo = malo;
1595392f7a3SLiteSpeed Tech    if (!pow)
1605392f7a3SLiteSpeed Tech        page->full_slot_mask = (1ULL << (0x1000 / obj_size)) - 1;
1615392f7a3SLiteSpeed Tech    else if (nbits == MALO_MIN_NBITS)
16250aadb33SDmitri Tikhonov        page->full_slot_mask = ~0ULL;
16350aadb33SDmitri Tikhonov    else
16450aadb33SDmitri Tikhonov        page->full_slot_mask = (1ULL << (1 << (12 - nbits))) - 1;
16550aadb33SDmitri Tikhonov    page->slots = (1ULL << n_slots) - 1;
1665392f7a3SLiteSpeed Tech    page->pow = pow;
1675392f7a3SLiteSpeed Tech    page->nbits = pow ? nbits : obj_size;
16850aadb33SDmitri Tikhonov    page->initial_slot = n_slots;
16950aadb33SDmitri Tikhonov
17050aadb33SDmitri Tikhonov    return malo;
1715392f7a3SLiteSpeed Tech#else
1725392f7a3SLiteSpeed Tech    struct malo *malo;
1735392f7a3SLiteSpeed Tech
1745392f7a3SLiteSpeed Tech    /* Use the same sizing mechanism as in the normal version */
1755392f7a3SLiteSpeed Tech    if (obj_size < (1 << MALO_MIN_NBITS))
1765392f7a3SLiteSpeed Tech        obj_size = 1 << MALO_MIN_NBITS;
1775392f7a3SLiteSpeed Tech    else
1785392f7a3SLiteSpeed Tech        obj_size = (obj_size + 7) & -8;
1795392f7a3SLiteSpeed Tech
1805392f7a3SLiteSpeed Tech    malo = malloc(sizeof(*malo));
1815392f7a3SLiteSpeed Tech    if (malo)
1825392f7a3SLiteSpeed Tech    {
1835392f7a3SLiteSpeed Tech        TAILQ_INIT(&malo->elems);
1845392f7a3SLiteSpeed Tech        malo->obj_size = obj_size;
185b1a7c3f9SDmitri Tikhonov        malo->next_iter_elem = NULL;
1865392f7a3SLiteSpeed Tech        return malo;
1875392f7a3SLiteSpeed Tech    }
1885392f7a3SLiteSpeed Tech    else
1895392f7a3SLiteSpeed Tech        return NULL;
1905392f7a3SLiteSpeed Tech#endif
19150aadb33SDmitri Tikhonov}
19250aadb33SDmitri Tikhonov
19350aadb33SDmitri Tikhonov
1945392f7a3SLiteSpeed Tech#if LSQUIC_USE_POOLS
19550aadb33SDmitri Tikhonovstatic struct malo_page *
19650aadb33SDmitri Tikhonovallocate_page (struct malo *malo)
19750aadb33SDmitri Tikhonov{
19850aadb33SDmitri Tikhonov    struct malo_page *page;
19950aadb33SDmitri Tikhonov    if (0 != posix_memalign((void **) &page, 0x1000, 0x1000))
20050aadb33SDmitri Tikhonov        return NULL;
20150aadb33SDmitri Tikhonov    SLIST_INSERT_HEAD(&malo->all_pages, page, next_page);
20250aadb33SDmitri Tikhonov    LIST_INSERT_HEAD(&malo->free_pages, page, next_free_page);
20350aadb33SDmitri Tikhonov    page->slots = 1;
20450aadb33SDmitri Tikhonov    page->full_slot_mask = malo->page_header.full_slot_mask;
20550aadb33SDmitri Tikhonov    page->nbits = malo->page_header.nbits;
2065392f7a3SLiteSpeed Tech    page->pow = malo->page_header.pow;
20750aadb33SDmitri Tikhonov    page->malo = malo;
20850aadb33SDmitri Tikhonov    page->initial_slot = 1;
20950aadb33SDmitri Tikhonov    return page;
21050aadb33SDmitri Tikhonov}
2115392f7a3SLiteSpeed Tech#endif
21250aadb33SDmitri Tikhonov
21350aadb33SDmitri Tikhonov
21450aadb33SDmitri Tikhonov#define FAIL_NOMEM do { errno = ENOMEM; return NULL; } while (0)
21550aadb33SDmitri Tikhonov
21650aadb33SDmitri Tikhonov/* Get a new object. */
21750aadb33SDmitri Tikhonovvoid *
21850aadb33SDmitri Tikhonovlsquic_malo_get (struct malo *malo)
21950aadb33SDmitri Tikhonov{
2205392f7a3SLiteSpeed Tech#if LSQUIC_USE_POOLS
22150aadb33SDmitri Tikhonov    fiu_do_on("malo/get", FAIL_NOMEM);
22250aadb33SDmitri Tikhonov    struct malo_page *page = LIST_FIRST(&malo->free_pages);
22350aadb33SDmitri Tikhonov    if (!page)
22450aadb33SDmitri Tikhonov    {
22550aadb33SDmitri Tikhonov        page = allocate_page(malo);
22650aadb33SDmitri Tikhonov        if (!page)
22750aadb33SDmitri Tikhonov            return NULL;
22850aadb33SDmitri Tikhonov    }
22950aadb33SDmitri Tikhonov    unsigned slot = find_free_slot(page->slots);
23050aadb33SDmitri Tikhonov    page->slots |= (1ULL << slot);
23150aadb33SDmitri Tikhonov    if (page->full_slot_mask == page->slots)
23250aadb33SDmitri Tikhonov        LIST_REMOVE(page, next_free_page);
2335392f7a3SLiteSpeed Tech    if (page->pow)
2345392f7a3SLiteSpeed Tech        return (char *) page + (slot << page->nbits);
2355392f7a3SLiteSpeed Tech    else
2365392f7a3SLiteSpeed Tech        return (char *) page + (slot * page->nbits);
2375392f7a3SLiteSpeed Tech#else
2385392f7a3SLiteSpeed Tech    struct nopool_elem *el;
2395392f7a3SLiteSpeed Tech    el = malloc(sizeof(*el) + malo->obj_size);
2405392f7a3SLiteSpeed Tech    if (el)
2415392f7a3SLiteSpeed Tech    {
2425392f7a3SLiteSpeed Tech        TAILQ_INSERT_HEAD(&malo->elems, el, next);
2435392f7a3SLiteSpeed Tech        el->malo = malo;
2445392f7a3SLiteSpeed Tech        return el->data;
2455392f7a3SLiteSpeed Tech    }
2465392f7a3SLiteSpeed Tech    else
2475392f7a3SLiteSpeed Tech        return NULL;
2485392f7a3SLiteSpeed Tech#endif
24950aadb33SDmitri Tikhonov}
25050aadb33SDmitri Tikhonov
25150aadb33SDmitri Tikhonov
25250aadb33SDmitri Tikhonov/* Return obj to the pool */
25350aadb33SDmitri Tikhonovvoid
25450aadb33SDmitri Tikhonovlsquic_malo_put (void *obj)
25550aadb33SDmitri Tikhonov{
2565392f7a3SLiteSpeed Tech#if LSQUIC_USE_POOLS
25750aadb33SDmitri Tikhonov    uintptr_t page_addr = (uintptr_t) obj & ~((1 << 12) - 1);
25850aadb33SDmitri Tikhonov    struct malo_page *page = (void *) page_addr;
2595392f7a3SLiteSpeed Tech    unsigned slot;
2605392f7a3SLiteSpeed Tech    if (page->pow)
2615392f7a3SLiteSpeed Tech        slot = ((uintptr_t) obj - page_addr) >> page->nbits;
2625392f7a3SLiteSpeed Tech    else
2635392f7a3SLiteSpeed Tech        slot = ((uintptr_t) obj - page_addr) / page->nbits;
26450aadb33SDmitri Tikhonov    if (page->full_slot_mask == page->slots)
26550aadb33SDmitri Tikhonov        LIST_INSERT_HEAD(&page->malo->free_pages, page, next_free_page);
26650aadb33SDmitri Tikhonov    page->slots &= ~(1ULL << slot);
2675392f7a3SLiteSpeed Tech#else
2685392f7a3SLiteSpeed Tech    struct nopool_elem *el;
2695392f7a3SLiteSpeed Tech    el = (struct nopool_elem *) ((char *) obj - sizeof(*el));
2705392f7a3SLiteSpeed Tech    if (el == el->malo->next_iter_elem)
2715392f7a3SLiteSpeed Tech        el->malo->next_iter_elem = TAILQ_NEXT(el->malo->next_iter_elem, next);
2725392f7a3SLiteSpeed Tech    TAILQ_REMOVE(&el->malo->elems, el, next);
2735392f7a3SLiteSpeed Tech    free(el);
2745392f7a3SLiteSpeed Tech#endif
27550aadb33SDmitri Tikhonov}
27650aadb33SDmitri Tikhonov
27750aadb33SDmitri Tikhonov
27850aadb33SDmitri Tikhonovvoid
27950aadb33SDmitri Tikhonovlsquic_malo_destroy (struct malo *malo)
28050aadb33SDmitri Tikhonov{
2815392f7a3SLiteSpeed Tech#if LSQUIC_USE_POOLS
28250aadb33SDmitri Tikhonov    struct malo_page *page, *next;
28350aadb33SDmitri Tikhonov    page = SLIST_FIRST(&malo->all_pages);
28450aadb33SDmitri Tikhonov    while (page != &malo->page_header)
28550aadb33SDmitri Tikhonov    {
28650aadb33SDmitri Tikhonov        next = SLIST_NEXT(page, next_page);
287461e84d8SAmol Deshpande#ifndef WIN32
28850aadb33SDmitri Tikhonov        free(page);
289461e84d8SAmol Deshpande#else
290461e84d8SAmol Deshpande        _aligned_free(page);
291461e84d8SAmol Deshpande#endif
29250aadb33SDmitri Tikhonov        page = next;
29350aadb33SDmitri Tikhonov    }
294461e84d8SAmol Deshpande#ifndef WIN32
29550aadb33SDmitri Tikhonov    free(page);
296461e84d8SAmol Deshpande#else
297461e84d8SAmol Deshpande        _aligned_free(page);
298461e84d8SAmol Deshpande#endif
2995392f7a3SLiteSpeed Tech#else
3005392f7a3SLiteSpeed Tech    struct nopool_elem *el, *next_el;
3015392f7a3SLiteSpeed Tech    for (el = TAILQ_FIRST(&malo->elems); el; el = next_el)
3025392f7a3SLiteSpeed Tech    {
3035392f7a3SLiteSpeed Tech        next_el = TAILQ_NEXT(el, next);
3045392f7a3SLiteSpeed Tech        free(el);
3055392f7a3SLiteSpeed Tech    }
3065392f7a3SLiteSpeed Tech    free(malo);
3075392f7a3SLiteSpeed Tech#endif
30850aadb33SDmitri Tikhonov}
30950aadb33SDmitri Tikhonov
31050aadb33SDmitri Tikhonov
31150aadb33SDmitri Tikhonov/* The iterator is built-in.  Usage:
31250aadb33SDmitri Tikhonov * void *obj;
31350aadb33SDmitri Tikhonov * for (obj = lsquic_malo_first(malo); obj; lsquic_malo_next(malo))
31450aadb33SDmitri Tikhonov *     do_stuff(obj);
31550aadb33SDmitri Tikhonov */
31650aadb33SDmitri Tikhonovvoid *
31750aadb33SDmitri Tikhonovlsquic_malo_first (struct malo *malo)
31850aadb33SDmitri Tikhonov{
3195392f7a3SLiteSpeed Tech#if LSQUIC_USE_POOLS
32050aadb33SDmitri Tikhonov    malo->iter.cur_page = SLIST_FIRST(&malo->all_pages);
32150aadb33SDmitri Tikhonov    malo->iter.next_slot = malo->iter.cur_page->initial_slot;
3225392f7a3SLiteSpeed Tech#else
3235392f7a3SLiteSpeed Tech    malo->next_iter_elem = TAILQ_FIRST(&malo->elems);
3245392f7a3SLiteSpeed Tech#endif
32550aadb33SDmitri Tikhonov    return lsquic_malo_next(malo);
32650aadb33SDmitri Tikhonov}
32750aadb33SDmitri Tikhonov
32850aadb33SDmitri Tikhonov
32950aadb33SDmitri Tikhonovvoid *
33050aadb33SDmitri Tikhonovlsquic_malo_next (struct malo *malo)
33150aadb33SDmitri Tikhonov{
3325392f7a3SLiteSpeed Tech#if LSQUIC_USE_POOLS
33350aadb33SDmitri Tikhonov    struct malo_page *page;
33450aadb33SDmitri Tikhonov    unsigned max_slot, slot;
33550aadb33SDmitri Tikhonov
33650aadb33SDmitri Tikhonov    page = malo->iter.cur_page;
33750aadb33SDmitri Tikhonov    if (page)
33850aadb33SDmitri Tikhonov    {
3395392f7a3SLiteSpeed Tech        if (page->pow)
3405392f7a3SLiteSpeed Tech            max_slot = 1 << (12 - page->nbits);     /* Same for all pages */
3415392f7a3SLiteSpeed Tech        else
3425392f7a3SLiteSpeed Tech            max_slot = 0x1000 / page->nbits;
34350aadb33SDmitri Tikhonov        slot = malo->iter.next_slot;
34450aadb33SDmitri Tikhonov        while (1)
34550aadb33SDmitri Tikhonov        {
34650aadb33SDmitri Tikhonov            for (; slot < max_slot; ++slot)
34750aadb33SDmitri Tikhonov            {
34850aadb33SDmitri Tikhonov                if (page->slots & (1ULL << slot))
34950aadb33SDmitri Tikhonov                {
35050aadb33SDmitri Tikhonov                    malo->iter.cur_page  = page;
35150aadb33SDmitri Tikhonov                    malo->iter.next_slot = slot + 1;
3525392f7a3SLiteSpeed Tech                    if (page->pow)
3535392f7a3SLiteSpeed Tech                        return (char *) page + (slot << page->nbits);
3545392f7a3SLiteSpeed Tech                    else
3555392f7a3SLiteSpeed Tech                    {
3565392f7a3SLiteSpeed Tech                        assert(slot * (page->nbits + 1) < 0x1000);
3575392f7a3SLiteSpeed Tech                        return (char *) page + (slot * page->nbits);
3585392f7a3SLiteSpeed Tech                    }
35950aadb33SDmitri Tikhonov                }
36050aadb33SDmitri Tikhonov            }
36150aadb33SDmitri Tikhonov            page = SLIST_NEXT(page, next_page);
36250aadb33SDmitri Tikhonov            if (page)
36350aadb33SDmitri Tikhonov                slot = page->initial_slot;
36450aadb33SDmitri Tikhonov            else
36550aadb33SDmitri Tikhonov            {
36650aadb33SDmitri Tikhonov                malo->iter.cur_page = NULL;     /* Stop iterator */
36750aadb33SDmitri Tikhonov                return NULL;
36850aadb33SDmitri Tikhonov            }
36950aadb33SDmitri Tikhonov        }
37050aadb33SDmitri Tikhonov    }
37150aadb33SDmitri Tikhonov
37250aadb33SDmitri Tikhonov    return NULL;
3735392f7a3SLiteSpeed Tech#else
3745392f7a3SLiteSpeed Tech    struct nopool_elem *retval;
3755392f7a3SLiteSpeed Tech
3765392f7a3SLiteSpeed Tech    if (malo->next_iter_elem)
3775392f7a3SLiteSpeed Tech    {
3785392f7a3SLiteSpeed Tech        retval = malo->next_iter_elem;
3795392f7a3SLiteSpeed Tech        malo->next_iter_elem = TAILQ_NEXT(malo->next_iter_elem, next);
3805392f7a3SLiteSpeed Tech        return retval->data;
3815392f7a3SLiteSpeed Tech    }
3825392f7a3SLiteSpeed Tech    else
3835392f7a3SLiteSpeed Tech        return NULL;
3845392f7a3SLiteSpeed Tech#endif
38550aadb33SDmitri Tikhonov}
38650aadb33SDmitri Tikhonov
38750aadb33SDmitri Tikhonov
3885392f7a3SLiteSpeed Tech#if LSQUIC_USE_POOLS
38950aadb33SDmitri Tikhonovstatic unsigned
39050aadb33SDmitri Tikhonovsize_in_bits (size_t sz)
39150aadb33SDmitri Tikhonov{
39250aadb33SDmitri Tikhonov#if __GNUC__
3935392f7a3SLiteSpeed Tech    unsigned clz = sz > 1 ? __builtin_clz(sz - 1) : 31;
39450aadb33SDmitri Tikhonov    return 32 - clz;
3952d296031SDmitri Tikhonov#elif defined(WIN32)
3962d296031SDmitri Tikhonov    unsigned char s;
3972d296031SDmitri Tikhonov    unsigned long idx;
3982d296031SDmitri Tikhonov    s = _BitScanReverse(&idx, sz);
3992d296031SDmitri Tikhonov    assert(s);
4002d296031SDmitri Tikhonov    return (unsigned) idx + 1;
40150aadb33SDmitri Tikhonov#else
4022d296031SDmitri Tikhonov#error This function contains a bug!
40350aadb33SDmitri Tikhonov    unsigned clz;
40450aadb33SDmitri Tikhonov    size_t y;
40550aadb33SDmitri Tikhonov
40650aadb33SDmitri Tikhonov    --sz;
40750aadb33SDmitri Tikhonov    clz = 32;
40850aadb33SDmitri Tikhonov    y = sz >> 16;   if (y) { clz -= 16; sz = y; }
40950aadb33SDmitri Tikhonov    y = sz >>  8;   if (y) { clz -=  8; sz = y; }
41050aadb33SDmitri Tikhonov    y = sz >>  4;   if (y) { clz -=  4; sz = y; }
41150aadb33SDmitri Tikhonov    y = sz >>  2;   if (y) { clz -=  2; sz = y; }
41250aadb33SDmitri Tikhonov    y = sz >>  1;   if (y) return 32 - clz + 1;
41350aadb33SDmitri Tikhonov    return 32 - clz + sz;
41450aadb33SDmitri Tikhonov#endif
41550aadb33SDmitri Tikhonov}
41650aadb33SDmitri Tikhonov
41750aadb33SDmitri Tikhonov
41850aadb33SDmitri Tikhonovstatic unsigned
41950aadb33SDmitri Tikhonovfind_free_slot (uint64_t slots)
42050aadb33SDmitri Tikhonov{
42150aadb33SDmitri Tikhonov#if __GNUC__
42250aadb33SDmitri Tikhonov    return __builtin_ffsll(~slots) - 1;
42350aadb33SDmitri Tikhonov#else
42450aadb33SDmitri Tikhonov    unsigned n;
42550aadb33SDmitri Tikhonov
42650aadb33SDmitri Tikhonov    slots =~ slots;
42750aadb33SDmitri Tikhonov    n = 0;
42850aadb33SDmitri Tikhonov
42950aadb33SDmitri Tikhonov    if (0 == (slots & ((1ULL << 32) - 1))) { n += 32; slots >>= 32; }
43050aadb33SDmitri Tikhonov    if (0 == (slots & ((1ULL << 16) - 1))) { n += 16; slots >>= 16; }
43150aadb33SDmitri Tikhonov    if (0 == (slots & ((1ULL <<  8) - 1))) { n +=  8; slots >>=  8; }
43250aadb33SDmitri Tikhonov    if (0 == (slots & ((1ULL <<  4) - 1))) { n +=  4; slots >>=  4; }
43350aadb33SDmitri Tikhonov    if (0 == (slots & ((1ULL <<  2) - 1))) { n +=  2; slots >>=  2; }
43450aadb33SDmitri Tikhonov    if (0 == (slots & ((1ULL <<  1) - 1))) { n +=  1; slots >>=  1; }
43550aadb33SDmitri Tikhonov    return n;
43650aadb33SDmitri Tikhonov#endif
43750aadb33SDmitri Tikhonov}
4385392f7a3SLiteSpeed Tech#endif
439c51ce338SDmitri Tikhonov
440c51ce338SDmitri Tikhonov
441c51ce338SDmitri Tikhonovsize_t
442c51ce338SDmitri Tikhonovlsquic_malo_mem_used (const struct malo *malo)
443c51ce338SDmitri Tikhonov{
4445392f7a3SLiteSpeed Tech#if LSQUIC_USE_POOLS
445c51ce338SDmitri Tikhonov    const struct malo_page *page;
446c51ce338SDmitri Tikhonov    size_t size;
447c51ce338SDmitri Tikhonov
448c51ce338SDmitri Tikhonov    size = 0;
449c51ce338SDmitri Tikhonov    SLIST_FOREACH(page, &malo->all_pages, next_page)
450c51ce338SDmitri Tikhonov        size += sizeof(*page);
451c51ce338SDmitri Tikhonov
452c51ce338SDmitri Tikhonov    return size;
4535392f7a3SLiteSpeed Tech#else
4545392f7a3SLiteSpeed Tech    return 0;
4555392f7a3SLiteSpeed Tech#endif
456c51ce338SDmitri Tikhonov}
457