/* Copyright (c) 2017 - 2026 LiteSpeed Technologies Inc.  See LICENSE. */
/*
 * lsquic_senhist.h -- History sent packets.
 *
 * We need to keep track of packet numbers in order to verify ACKs.  To
 * speed up processing, we make sure that there is never a gap in the
 * packet number sequence we generate.
 */

#ifndef LSQUIC_SENHIST_H
#define LSQUIC_SENHIST_H 1

#ifndef LSQUIC_SENHIST_FATAL
#   define LSQUIC_SENHIST_FATAL 0
#endif

typedef struct lsquic_senhist {
    lsquic_packno_t             sh_last_sent;
    lsquic_packno_t             sh_warn_thresh;
    enum {
#if !LSQUIC_SENHIST_FATAL
        SH_WARNED   = 1 << 0,   /* Warn once */
#endif
        SH_GAP_OK   = 1 << 1,   /* Before connection is just about to close or
                                 * during mini/full packet handoff.
                                 */
    }                           sh_flags;
} lsquic_senhist_t;

static inline void
lsquic_senhist_init (lsquic_senhist_t *hist, unsigned is_ietf)
{
    if (is_ietf)
        (hist)->sh_last_sent = ~0ull;
    else
        (hist)->sh_last_sent = 0;
}

#define lsquic_senhist_cleanup(hist)

#if LSQUIC_SENHIST_FATAL
#define lsquic_senhist_add(hist, packno) do {                           \
    if (!((hist)->sh_flags & SH_GAP_OK)                                 \
                        && (packno) > (hist)->sh_warn_thresh)           \
        assert((hist)->sh_last_sent == packno - 1);                     \
    if ((int64_t) (packno) > (int64_t) (hist)->sh_last_sent)            \
        (hist)->sh_last_sent = packno;                                  \
} while (0)
#else
#define lsquic_senhist_add(hist, packno) do {                           \
    if ((hist)->sh_last_sent != packno - 1)                             \
    {                                                                   \
        if (!((hist)->sh_flags & (SH_WARNED|SH_GAP_OK))                 \
                        && (packno) > (hist)->sh_warn_thresh)           \
        {                                                               \
            LSQ_WARN("send history gap %"PRIu64" - %"PRIu64,            \
                (hist)->sh_last_sent, packno);                          \
            (hist)->sh_flags |= SH_WARNED;                              \
        }                                                               \
    }                                                                   \
    if ((int64_t) (packno) > (int64_t) (hist)->sh_last_sent)            \
        (hist)->sh_last_sent = packno;                                  \
} while (0)
#endif

/* Returns 0 if no packets have been sent yet */
#define lsquic_senhist_largest(hist) (+(hist)->sh_last_sent)

void
lsquic_senhist_tostr (lsquic_senhist_t *hist, char *buf, size_t bufsz);

static inline size_t
lsquic_senhist_mem_used(const lsquic_senhist_t *hist)
{
    return sizeof(*(hist));
}

#endif
