/* Copyright (c) 2017 - 2026 LiteSpeed Technologies Inc.  See LICENSE. */
/* Functions to resize packets */

#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>
#include <sys/queue.h>

#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h"
#include "lsquic_packet_resize.h"
#include "lsquic_parse.h"
#include "lsquic_hash.h"
#include "lsquic_varint.h"
#include "lsquic_hq.h"
#include "lsquic_sfcw.h"
#include "lsquic_stream.h"
#include "lsquic_mm.h"
#include "lsquic_engine_public.h"
#include "lsquic_conn.h"

#define LSQUIC_LOGGER_MODULE LSQLM_PACKET_RESIZE
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(prctx->prc_conn)
#include "lsquic_logger.h"


void
lsquic_packet_resize_init (struct packet_resize_ctx *prctx,
    struct lsquic_engine_public *enpub, struct lsquic_conn *lconn, void *ctx,
    const struct packet_resize_if *pr_if)
{
    memset(prctx, 0, sizeof(*prctx));
    prctx->prc_conn = lconn;
    prctx->prc_pri = pr_if;
    prctx->prc_enpub = enpub;
    prctx->prc_data = ctx;
    LSQ_DEBUG("initialized");
}


static const struct frame_rec *
packet_resize_next_frec (struct packet_resize_ctx *prctx)
{
    const struct frame_rec *frec;

    assert(!prctx->prc_cur_frec);
    if (prctx->prc_cur_packet)
    {
        LSQ_DEBUG("get next frec from current packet %"PRIu64,
                                            prctx->prc_cur_packet->po_packno);
        frec = lsquic_pofi_next(&prctx->prc_pofi);
        if (frec)
            return frec;
        LSQ_DEBUG("discard packet %"PRIu64, prctx->prc_cur_packet->po_packno);
        prctx->prc_pri->pri_discard_packet(prctx->prc_data,
                                                        prctx->prc_cur_packet);
        prctx->prc_cur_packet = NULL; /* Not necessary; just future-proofing */
    }

    do
    {
        prctx->prc_cur_packet = prctx->prc_pri->pri_next_packet(prctx->prc_data);
        if (!prctx->prc_cur_packet)
        {
            LSQ_DEBUG("out of input packets");
            return NULL;
        }
        frec = lsquic_pofi_first(&prctx->prc_pofi, prctx->prc_cur_packet);
        if (frec == NULL)
        {
            LSQ_DEBUG("discard, no good frec from current packet %"PRIu64,
                                        prctx->prc_cur_packet->po_packno);
            prctx->prc_pri->pri_discard_packet(prctx->prc_data,
                                                    prctx->prc_cur_packet);
        }
    } while (frec == NULL);
    LSQ_DEBUG("return first frec from new current packet %"PRIu64,
                                        prctx->prc_cur_packet->po_packno);
    return frec;
}


static const struct frame_rec *
packet_resize_get_frec (struct packet_resize_ctx *prctx)
{
    if (!prctx->prc_cur_frec)
    {
        prctx->prc_cur_frec = packet_resize_next_frec(prctx);
        if (prctx->prc_cur_frec)
            prctx->prc_flags |= PRC_NEW_FREC;
    }
    return prctx->prc_cur_frec;
}


static size_t
packet_resize_gsf_read (void *ctx, void *buf, size_t len, int *fin)
{
    struct packet_resize_ctx *const prctx = ctx;
    size_t left;

    left = (size_t) prctx->prc_data_frame.df_size
                                - (size_t) prctx->prc_data_frame.df_read_off;
    if (len > left)
        len = left;
    memcpy(buf,
        prctx->prc_data_frame.df_data + prctx->prc_data_frame.df_read_off, len);
    prctx->prc_data_frame.df_read_off += len;
    *fin = prctx->prc_data_frame.df_fin
        && prctx->prc_data_frame.df_size == prctx->prc_data_frame.df_read_off;

    return len;
}


struct lsquic_packet_out *
lsquic_packet_resize_next (struct packet_resize_ctx *prctx)
{
    const unsigned char *data_in;
    struct lsquic_packet_out *new;
    struct stream_frame stream_frame;
    const struct frame_rec *frec;
    int s, w, fin, parsed_len;
    size_t nbytes;

    if (frec = packet_resize_get_frec(prctx), frec == NULL)
        return NULL;

    new = prctx->prc_pri->pri_new_packet(prctx->prc_data);
    if (!new)
    {
        LSQ_DEBUG("cannot allocate new packet");
        goto err;
    }

  proc_frec:
    if ((1 << frec->fe_frame_type) & (QUIC_FTBIT_STREAM|QUIC_FTBIT_CRYPTO))
    {
        if (prctx->prc_flags & PRC_NEW_FREC)
        {
            data_in = prctx->prc_cur_packet->po_data + frec->fe_off;
            parsed_len = (&prctx->prc_conn->cn_pf->pf_parse_stream_frame)
                [frec->fe_frame_type == QUIC_FRAME_CRYPTO]
                (data_in, frec->fe_len, &stream_frame);
            if (parsed_len < 0)
            {
                LSQ_WARN("cannot parse %s frame",
                                        frame_type_2_str[frec->fe_frame_type]);
                goto err;
            }
            if ((unsigned) parsed_len != frec->fe_len)
            {
                LSQ_WARN("parsed %s frame size does not match frame record",
                                        frame_type_2_str[frec->fe_frame_type]);
                goto err;
            }
            prctx->prc_data_frame = stream_frame.data_frame;
            prctx->prc_flags &= ~PRC_NEW_FREC;
            LSQ_DEBUG("parsed %s frame record for stream %"PRIu64
                "; off: %"PRIu64"; size: %"PRIu16"; fin: %d",
                frame_type_2_str[frec->fe_frame_type],
                frec->fe_stream->id,
                stream_frame.data_frame.df_offset,
                stream_frame.data_frame.df_size,
                stream_frame.data_frame.df_fin);
        }
        fin = prctx->prc_data_frame.df_fin
            && prctx->prc_data_frame.df_read_off == prctx->prc_data_frame.df_size;
        nbytes = prctx->prc_data_frame.df_size - prctx->prc_data_frame.df_read_off;
        w = (&prctx->prc_conn->cn_pf->pf_gen_stream_frame)
                [frec->fe_frame_type == QUIC_FRAME_CRYPTO](
                new->po_data + new->po_data_sz, lsquic_packet_out_avail(new),
                frec->fe_stream->id,
                prctx->prc_data_frame.df_offset + prctx->prc_data_frame.df_read_off,
                fin, nbytes, packet_resize_gsf_read, prctx);
        if (w < 0)
        {
            /* We rely on stream-generating function returning an error instead
             * of pre-calculating required size and checking.
             */
            LSQ_DEBUG("cannot fit another %s frame, new packet done",
                frame_type_2_str[frec->fe_frame_type]);
            goto done;
        }
        if (0 != lsquic_packet_out_add_stream(new, &prctx->prc_enpub->enp_mm,
                        frec->fe_stream, frec->fe_frame_type,
                        new->po_data_sz, w))
        {
            LSQ_WARN("cannot add stream frame record to new packet");
            goto err;
        }
        new->po_data_sz += w;
        new->po_frame_types |= 1 << frec->fe_frame_type;
        if (0 == lsquic_packet_out_avail(new))
            new->po_flags |= PO_STREAM_END;
        if (prctx->prc_data_frame.df_size == prctx->prc_data_frame.df_read_off)
        {
            LSQ_DEBUG("finished using %s frame record",
                                        frame_type_2_str[frec->fe_frame_type]);
            --frec->fe_stream->n_unacked;
            frec = prctx->prc_cur_frec = NULL;
            if (lsquic_packet_out_avail(new) > 0)
                if (frec = packet_resize_get_frec(prctx), frec != NULL)
                    goto proc_frec;
        }
    }
    else if (prctx->prc_cur_frec->fe_len <= lsquic_packet_out_avail(new))
    {
        if ((1 << frec->fe_frame_type) & BQUIC_FRAME_REGEN_MASK)
        {
            if (new->po_regen_sz == new->po_data_sz)
                new->po_regen_sz += frec->fe_len;
            else
            {
                LSQ_DEBUG("got non-contiguous regen frame %s, packet done",
                                        frame_type_2_str[frec->fe_frame_type]);
                goto done;
            }
        }
        memcpy(new->po_data + new->po_data_sz,
            prctx->prc_cur_packet->po_data + frec->fe_off, frec->fe_len);
        if (frec->fe_frame_type == QUIC_FRAME_RST_STREAM)
            s = lsquic_packet_out_add_stream(new, &prctx->prc_enpub->enp_mm,
                        frec->fe_stream, frec->fe_frame_type,
                        new->po_data_sz, frec->fe_len);
        else
           s = lsquic_packet_out_add_frame(new, &prctx->prc_enpub->enp_mm,
                        frec->fe_u.data, frec->fe_frame_type,
                        new->po_data_sz, frec->fe_len);
        if (s != 0)
        {
            LSQ_WARN("cannot add %s frame record to new packet",
                                    frame_type_2_str[frec->fe_frame_type]);
            goto err;
        }
        new->po_data_sz += frec->fe_len;
        new->po_frame_types |= 1 << frec->fe_frame_type;
        LSQ_DEBUG("copy %hu-byte %s frame into new packet", frec->fe_len,
                                    frame_type_2_str[frec->fe_frame_type]);
        if (frec->fe_frame_type == QUIC_FRAME_RST_STREAM)
            --frec->fe_stream->n_unacked;
        frec = prctx->prc_cur_frec = NULL;
        if (lsquic_packet_out_avail(new) > 0)
            if (frec = packet_resize_get_frec(prctx), frec != NULL)
                goto proc_frec;
    }

  done:
    if (0 == new->po_data_sz)
    {
        LSQ_WARN("frame too large");
        goto err;
    }

    return new;

  err:
    if (new)
        lsquic_packet_out_destroy(new, prctx->prc_enpub,
                                            new->po_path->np_peer_ctx);
    prctx->prc_flags |= PRC_ERROR;
    return NULL;
}
