/* Copyright (c) 2017 - 2026 LiteSpeed Technologies Inc.  See LICENSE. */
/*
 * lsquic_rechist.c -- History of received packets.
 */

#include <assert.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <inttypes.h>

#include "lsquic_int_types.h"
#include "lsquic_rechist.h"


#define BITS_PER_MASK (sizeof(uintptr_t) * 8)

#if UINTPTR_MAX == 18446744073709551615UL
#define LOG2_BITS 6
#else
#define LOG2_BITS 5
#endif


void
lsquic_rechist_init (struct lsquic_rechist *rechist, int ietf,
                                                        unsigned max_ranges)
{
    memset(rechist, 0, sizeof(*rechist));
    rechist->rh_cutoff = ietf ? 0 : 1;
    /* '1' is an odd case that would add an extra conditional in
     * rechist_reuse_last_elem(), so we prohibit it.
     */
    if (max_ranges == 1)
        max_ranges = 2;
    rechist->rh_max_ranges = max_ranges;
}


void
lsquic_rechist_cleanup (lsquic_rechist_t *rechist)
{
    free(rechist->rh_elems);
    memset(rechist, 0, sizeof(*rechist));
}


static void
rechist_free_elem (struct lsquic_rechist *rechist, unsigned idx)
{
    rechist->rh_masks[idx >> LOG2_BITS] &=
                                ~(1ull << (idx & ((1u << LOG2_BITS) - 1)));
    --rechist->rh_n_used;
}


#define RE_HIGH(el_) ((el_)->re_low + (el_)->re_count - 1)

#if LSQUIC_TEST
static void
rechist_dump_elem(struct rechist_elem *el)
{
    fprintf(stderr, "[%" PRIu64 "-%" PRIu64 "]", RE_HIGH(el), el->re_low);
}

#ifdef __GNUC__
__attribute__((unused))
#endif
static void
rechist_dump(struct lsquic_rechist *rechist)
{
    fprintf(stderr,
            "%p: cutoff %" PRIu64 " l. acked %" PRIu64
            " masks %u alloced %u used %u max ranges %u head %u\n",
            rechist,
            rechist->rh_cutoff, 
            rechist->rh_largest_acked_received,
            rechist->rh_n_masks,
            rechist->rh_n_alloced,
            rechist->rh_n_used,
            rechist->rh_max_ranges,
            rechist->rh_head);

    if (rechist->rh_n_used)
    {
        int idx = rechist->rh_head;
        fprintf(stderr, " ");
        unsigned n_elems = 0;
        while (n_elems < rechist->rh_n_used)
        {
            ++n_elems;
            struct rechist_elem *el =  &rechist->rh_elems[idx];
            fprintf(stderr, " (%u)", idx); 
            rechist_dump_elem(el);            
            if (el->re_next == UINT_MAX)
                break;
            idx = el->re_next;
        }
        fprintf(stderr, "\n");
    }
}
#endif /* LSQUIC_TEST */

static unsigned
find_free_slot (uintptr_t slots)
{
#if __GNUC__
    if (slots)
#if UINTPTR_MAX == 18446744073709551615UL
        return __builtin_ctzll(~slots);
#else
        return __builtin_ctzl(~slots);
#endif
    else
        return 0;
#else
    unsigned n;

    slots =~ slots;
    n = 0;

#if UINTPTR_MAX == 18446744073709551615UL
    if (0 == (slots & ((1ULL << 32) - 1))) { n += 32; slots >>= 32; }
#endif
    if (0 == (slots & ((1ULL << 16) - 1))) { n += 16; slots >>= 16; }
    if (0 == (slots & ((1ULL <<  8) - 1))) { n +=  8; slots >>=  8; }
    if (0 == (slots & ((1ULL <<  4) - 1))) { n +=  4; slots >>=  4; }
    if (0 == (slots & ((1ULL <<  2) - 1))) { n +=  2; slots >>=  2; }
    if (0 == (slots & ((1ULL <<  1) - 1))) { n +=  1; slots >>=  1; }
    return n;
#endif
}


static int
rechist_grow (struct lsquic_rechist *rechist)
{
    unsigned n_masks, nelems;
    size_t size;
    ptrdiff_t moff;
    char *mem;

    moff = (char *) rechist->rh_masks - (char *) rechist->rh_elems;
    if (rechist->rh_n_alloced)
        nelems = rechist->rh_n_alloced * 2;
    else
        nelems = 4;
    if (rechist->rh_max_ranges && nelems > rechist->rh_max_ranges)
        nelems = rechist->rh_max_ranges;
    n_masks = (nelems + (-nelems & (BITS_PER_MASK - 1))) / BITS_PER_MASK;
    size = sizeof(struct rechist_elem) * nelems + sizeof(uintptr_t) * n_masks;
    mem = realloc(rechist->rh_elems, size);
    if (!mem)
        return -1;
    if (moff)
        memcpy(mem + size - n_masks * sizeof(rechist->rh_masks[0]),
            (char *) mem + moff,
            rechist->rh_n_masks * sizeof(rechist->rh_masks[0]));
    if (rechist->rh_n_masks < n_masks)
        memset(mem + nelems * sizeof(rechist->rh_elems[0])
            + rechist->rh_n_masks * sizeof(rechist->rh_masks[0]),
            0, (n_masks - rechist->rh_n_masks) * sizeof(rechist->rh_masks[0]));
    rechist->rh_n_alloced = nelems;
    rechist->rh_n_masks = n_masks;
    rechist->rh_elems = (void *) mem;
    rechist->rh_masks = (void *) (mem + size
                                    - n_masks * sizeof(rechist->rh_masks[0]));
    return 0;
}


/* We hit maximum number of elements.  To allocate a new element, we drop
 * the last element and return its index, reusing the slot.
 */
static int
rechist_reuse_last_elem (struct lsquic_rechist *rechist)
{
    struct rechist_elem *last, *penultimate;
    unsigned last_idx;

    /* No need to check bitmask anywhere: the array is full! */
    last = rechist->rh_elems;
    while (last->re_next != UINT_MAX)
        ++last;

    last_idx = last - rechist->rh_elems;
    penultimate = rechist->rh_elems;
    while (penultimate->re_next != last_idx)
        ++penultimate;

    penultimate->re_next = UINT_MAX;
    return last_idx;
}


static int
rechist_alloc_elem (struct lsquic_rechist *rechist)
{
    unsigned i, idx;
    uintptr_t *mask;

    if (rechist->rh_n_used == rechist->rh_n_alloced)
    {
        if (rechist->rh_max_ranges
                            && rechist->rh_n_used >= rechist->rh_max_ranges)
            return rechist_reuse_last_elem(rechist);
        if (0 != rechist_grow(rechist))
            return -1;
    }

    for (mask = rechist->rh_masks; *mask == UINTPTR_MAX; ++mask)
        ;

    i = mask - rechist->rh_masks;
    assert(i < rechist->rh_n_masks);

    idx = find_free_slot(*mask);
    *mask |= 1ull << idx;
    ++rechist->rh_n_used;
    /* Note that re_next is invalid at this point, caller must set it */

    return idx + i * BITS_PER_MASK;
}


#if LSQUIC_TEST
/* When compiled as unit test, run sanity check every 127 operations
 * (127 is better than 128, as the latter aligns too well with the
 * regular rechist data structure sizes).
 */
static void
rechist_test_sanity (const struct lsquic_rechist *rechist)
{
    const struct rechist_elem *el;
    ptrdiff_t idx;
    uint64_t *masks;
    unsigned n_elems;

    masks = calloc(rechist->rh_n_masks, sizeof(masks[0]));

    n_elems = 0;
    if (rechist->rh_n_used)
    {
        el = &rechist->rh_elems[rechist->rh_head];
        while (1)
        {
            ++n_elems;
            idx = el - rechist->rh_elems;
            if (n_elems > rechist->rh_n_alloced
                || idx >=  rechist->rh_n_alloced)
                break;
            masks[idx >> LOG2_BITS] |= 1ull << (idx & ((1u << LOG2_BITS) - 1));
            if (el->re_next != UINT_MAX)
                el = &rechist->rh_elems[el->re_next];
            else
                break;
        }
    }

    assert(rechist->rh_n_used == n_elems);
    assert(0 == memcmp(masks, rechist->rh_masks,
                                    sizeof(masks[0]) * rechist->rh_n_masks));
    free(masks);
}
#define rechist_sanity_check(rechist_) do {                         \
        rechist_test_sanity(rechist_);                              \
} while (0)
#else
#define rechist_sanity_check(rechist)
#endif


enum received_st
lsquic_rechist_received (lsquic_rechist_t *rechist, lsquic_packno_t packno,
                         lsquic_time_t now)
{
    struct rechist_elem *el, *prev;
    ptrdiff_t next_idx, prev_idx;
    int idx;

    if (rechist->rh_n_used == 0)
        goto first_elem;

    if (packno < rechist->rh_cutoff)
        return REC_ST_DUP;

    el = &rechist->rh_elems[rechist->rh_head];
    prev = NULL;

    if (packno > RE_HIGH(el))
        rechist->rh_largest_acked_received = now;

    while (1)
    {
        if (packno > RE_HIGH(el) + 1)
            goto insert_before;
        if (packno == el->re_low - 1)
        {
            --el->re_low;
            ++el->re_count;
            if (el->re_next != UINT_MAX
                && el->re_low == RE_HIGH(&rechist->rh_elems[el->re_next]) + 1)
            {
                rechist_free_elem(rechist, el->re_next);
                el->re_count += rechist->rh_elems[el->re_next].re_count;
                el->re_low    = rechist->rh_elems[el->re_next].re_low;
                el->re_next   = rechist->rh_elems[el->re_next].re_next;
            }
            rechist_sanity_check(rechist);
            return REC_ST_OK;
        }
        if (packno == RE_HIGH(el) + 1)
        {
            ++el->re_count;
            rechist_sanity_check(rechist);
            return REC_ST_OK;
        }
        if (packno >= el->re_low && packno <= RE_HIGH(el))
            return REC_ST_DUP;
        if (el->re_next == UINT_MAX)
            break;  /* insert tail */
        prev = el;
        el = &rechist->rh_elems[el->re_next];
    }

    if (rechist->rh_max_ranges && rechist->rh_n_used >= rechist->rh_max_ranges)
        goto replace_last_el;
    prev_idx = el - rechist->rh_elems;
    idx = rechist_alloc_elem(rechist);
    if (idx < 0)
        return REC_ST_ERR;

    rechist->rh_elems[idx].re_low   = packno;
    rechist->rh_elems[idx].re_count = 1;
    rechist->rh_elems[idx].re_next  = UINT_MAX;
    rechist->rh_elems[prev_idx].re_next  = idx;
    rechist_sanity_check(rechist);
    return REC_ST_OK;

  first_elem:
    if (packno < rechist->rh_cutoff)
        return REC_ST_ERR;
    idx = rechist_alloc_elem(rechist);
    if (idx < 0)
        return REC_ST_ERR;

    rechist->rh_elems[idx].re_low   = packno;
    rechist->rh_elems[idx].re_count = 1;
    rechist->rh_elems[idx].re_next  = UINT_MAX;
    rechist->rh_head = idx;
    rechist->rh_largest_acked_received = now;
    rechist_sanity_check(rechist);
    return REC_ST_OK;

  insert_before:
    if (el->re_next == UINT_MAX && rechist->rh_max_ranges
                            && rechist->rh_n_used >= rechist->rh_max_ranges)
        goto replace_last_el;
    prev_idx = prev - rechist->rh_elems;
    next_idx = el - rechist->rh_elems;
    idx = rechist_alloc_elem(rechist);
    if (idx < 0)
        return REC_ST_ERR;

    rechist->rh_elems[idx].re_low   = packno;
    rechist->rh_elems[idx].re_count = 1;
    rechist->rh_elems[idx].re_next  = next_idx;
    if (next_idx == (ptrdiff_t)rechist->rh_head)
        rechist->rh_head = idx;
    else
        rechist->rh_elems[prev_idx].re_next = idx;

    rechist_sanity_check(rechist);
    return REC_ST_OK;

  replace_last_el:
    /* Special case: replace last element if chopping, because we cannot
     * realloc the "prev_idx" hook
     */
    assert(el->re_next == UINT_MAX);
    el->re_low   = packno;
    el->re_count = 1;
    rechist_sanity_check(rechist);
    return REC_ST_OK;
}


void
lsquic_rechist_stop_wait (lsquic_rechist_t *rechist, lsquic_packno_t cutoff)
{
    struct rechist_elem *el, *prev;

    if (rechist->rh_flags & RH_CUTOFF_SET)
    {
        assert(cutoff >= rechist->rh_cutoff);  /* Check performed in full_conn */
        if (cutoff <= rechist->rh_cutoff)
            return;
    }

    rechist->rh_cutoff = cutoff;
    rechist->rh_flags |= RH_CUTOFF_SET;

    if (rechist->rh_n_used == 0)
        return;

    el = &rechist->rh_elems[rechist->rh_head];
    prev = NULL;
    while (1)
    {
        if (cutoff > RE_HIGH(el))
        {
            if (prev)
                prev->re_next = UINT_MAX;
            break;
        }
        else if (cutoff > el->re_low)
        {
            el->re_count = RE_HIGH(el) - cutoff + 1;
            el->re_low = cutoff;
            if (el->re_next != UINT_MAX)
            {
                prev = el;
                el = &rechist->rh_elems[el->re_next];
                prev->re_next = UINT_MAX;
                break;
            }
            else
                goto end;
        }
        else if (el->re_next == UINT_MAX)
            goto end;
        prev = el;
        el = &rechist->rh_elems[el->re_next];
    }

    assert(el);
    while (1)
    {
        rechist_free_elem(rechist, el - rechist->rh_elems);
        if (el->re_next != UINT_MAX)
            el = &rechist->rh_elems[el->re_next];
        else
            break;
    }

  end:
    rechist_sanity_check(rechist);
}


lsquic_packno_t
lsquic_rechist_largest_packno (const lsquic_rechist_t *rechist)
{
    if (rechist->rh_n_used)
        return RE_HIGH(&rechist->rh_elems[rechist->rh_head]);
    else
        return 0;   /* Don't call this function if history is empty */
}


lsquic_packno_t
lsquic_rechist_cutoff (const lsquic_rechist_t *rechist)
{
    if (rechist->rh_flags & RH_CUTOFF_SET)
        return rechist->rh_cutoff;
    else
        return 0;
}


lsquic_time_t
lsquic_rechist_largest_recv (const lsquic_rechist_t *rechist)
{
    return rechist->rh_largest_acked_received;
}


const struct lsquic_packno_range *
lsquic_rechist_first (lsquic_rechist_t *rechist)
{
    unsigned idx;

    if (rechist->rh_n_used)
    {
        idx = rechist->rh_head;
        rechist->rh_iter.range.low  = rechist->rh_elems[idx].re_low;
        rechist->rh_iter.range.high = RE_HIGH(&rechist->rh_elems[idx]);
        rechist->rh_iter.next       = rechist->rh_elems[idx].re_next;
        return &rechist->rh_iter.range;
    }
    else
        return NULL;
}


const struct lsquic_packno_range *
lsquic_rechist_next (lsquic_rechist_t *rechist)
{
    unsigned idx;

    idx = rechist->rh_iter.next;
    if (idx != UINT_MAX)
    {
        rechist->rh_iter.range.low  = rechist->rh_elems[idx].re_low;
        rechist->rh_iter.range.high = RE_HIGH(&rechist->rh_elems[idx]);
        rechist->rh_iter.next       = rechist->rh_elems[idx].re_next;
        return &rechist->rh_iter.range;
    }
    else
        return NULL;
}


size_t
lsquic_rechist_mem_used (const struct lsquic_rechist *rechist)
{
    return sizeof(*rechist)
         + rechist->rh_n_alloced * sizeof(rechist->rh_elems[0])
         + rechist->rh_n_masks * sizeof(rechist->rh_masks[0]);
}


const struct lsquic_packno_range *
lsquic_rechist_peek (struct lsquic_rechist *rechist)
{
    if (rechist->rh_n_used)
    {
        rechist->rh_iter.range.low
                            = rechist->rh_elems[rechist->rh_head].re_low;
        rechist->rh_iter.range.high
                            = RE_HIGH(&rechist->rh_elems[rechist->rh_head]);
        return &rechist->rh_iter.range;
    }
    else
        return NULL;
}


int
lsquic_rechist_copy_ranges (struct lsquic_rechist *rechist, void *src_rechist,
    const struct lsquic_packno_range * (*first) (void *),
    const struct lsquic_packno_range * (*next) (void *))
{
    const struct lsquic_packno_range *range;
    struct rechist_elem *el;
    unsigned prev_idx;
    int idx;

    /* This function only works if rechist contains no elements */
    assert(rechist->rh_n_used == 0);

    prev_idx = UINT_MAX;
    for (range = first(src_rechist); range &&
            /* Do not overwrite higher-numbered ranges.  (Also, logic below
             * does not work if rechist_reuse_last_elem() is used.)
             */
            (rechist->rh_max_ranges == 0
                            || rechist->rh_n_used < rechist->rh_max_ranges);
                                                    range = next(src_rechist))
    {
        idx = rechist_alloc_elem(rechist);
        if (idx < 0)
            return -1;
        el = &rechist->rh_elems[idx];
        el->re_low = range->low;
        el->re_count = range->high - range->low + 1;
        el->re_next = UINT_MAX;
        if (prev_idx == UINT_MAX)
            rechist->rh_head = idx;
        else
            rechist->rh_elems[prev_idx].re_next = idx;
        prev_idx = idx;
    }

    return 0;
}
