/* inflate_p.h -- Private inline functions and macros shared with more than one deflate method
 *
 */

#ifndef INFLATE_P_H
#define INFLATE_P_H

#include <stdlib.h>

/* Architecture-specific hooks. */
#ifdef S390_DFLTCC_INFLATE
#  include "arch/s390/dfltcc_inflate.h"
#else
/* Memory management for the inflate state. Useful for allocating arch-specific extension blocks. */
#  define ZALLOC_INFLATE_STATE(strm) ((struct inflate_state *)ZALLOC(strm, 1, sizeof(struct inflate_state)))
#  define ZFREE_STATE(strm, addr) ZFREE(strm, addr)
#  define ZCOPY_INFLATE_STATE(dst, src) memcpy(dst, src, sizeof(struct inflate_state))
/* Memory management for the window. Useful for allocation the aligned window. */
#  define ZALLOC_WINDOW(strm, items, size) ZALLOC(strm, items, size)
#  define ZFREE_WINDOW(strm, addr) ZFREE(strm, addr)
/* Invoked at the end of inflateResetKeep(). Useful for initializing arch-specific extension blocks. */
#  define INFLATE_RESET_KEEP_HOOK(strm) do {} while (0)
/* Invoked at the beginning of inflatePrime(). Useful for updating arch-specific buffers. */
#  define INFLATE_PRIME_HOOK(strm, bits, value) do {} while (0)
/* Invoked at the beginning of each block. Useful for plugging arch-specific inflation code. */
#  define INFLATE_TYPEDO_HOOK(strm, flush) do {} while (0)
/* Returns whether zlib-ng should compute a checksum. Set to 0 if arch-specific inflation code already does that. */
#  define INFLATE_NEED_CHECKSUM(strm) 1
/* Returns whether zlib-ng should update a window. Set to 0 if arch-specific inflation code already does that. */
#  define INFLATE_NEED_UPDATEWINDOW(strm) 1
/* Invoked at the beginning of inflateMark(). Useful for updating arch-specific pointers and offsets. */
#  define INFLATE_MARK_HOOK(strm) do {} while (0)
/* Invoked at the beginning of inflateSyncPoint(). Useful for performing arch-specific state checks. */
#  define INFLATE_SYNC_POINT_HOOK(strm) do {} while (0)
#endif

/*
 *   Macros shared by inflate() and inflateBack()
 */

/* check function to use adler32() for zlib or crc32() for gzip */
#ifdef GUNZIP
#  define UPDATE(check, buf, len) \
    (state->flags ? PREFIX(crc32)(check, buf, len) : functable.adler32(check, buf, len))
#else
#  define UPDATE(check, buf, len) functable.adler32(check, buf, len)
#endif

/* check macros for header crc */
#ifdef GUNZIP
#  define CRC2(check, word) \
    do { \
        hbuf[0] = (unsigned char)(word); \
        hbuf[1] = (unsigned char)((word) >> 8); \
        check = PREFIX(crc32)(check, hbuf, 2); \
    } while (0)

#  define CRC4(check, word) \
    do { \
        hbuf[0] = (unsigned char)(word); \
        hbuf[1] = (unsigned char)((word) >> 8); \
        hbuf[2] = (unsigned char)((word) >> 16); \
        hbuf[3] = (unsigned char)((word) >> 24); \
        check = PREFIX(crc32)(check, hbuf, 4); \
    } while (0)
#endif

/* Load registers with state in inflate() for speed */
#define LOAD() \
    do { \
        put = strm->next_out; \
        left = strm->avail_out; \
        next = strm->next_in; \
        have = strm->avail_in; \
        hold = state->hold; \
        bits = state->bits; \
    } while (0)

/* Restore state from registers in inflate() */
#define RESTORE() \
    do { \
        strm->next_out = put; \
        strm->avail_out = left; \
        strm->next_in = (z_const unsigned char *)next; \
        strm->avail_in = have; \
        state->hold = hold; \
        state->bits = bits; \
    } while (0)

/* Clear the input bit accumulator */
#define INITBITS() \
    do { \
        hold = 0; \
        bits = 0; \
    } while (0)

/* Ensure that there is at least n bits in the bit accumulator.  If there is
   not enough available input to do that, then return from inflate()/inflateBack(). */
#define NEEDBITS(n) \
    do { \
        while (bits < (unsigned)(n)) \
            PULLBYTE(); \
    } while (0)

/* Return the low n bits of the bit accumulator (n < 16) */
#define BITS(n) \
    (hold & ((1U << (unsigned)(n)) - 1))

/* Remove n bits from the bit accumulator */
#define DROPBITS(n) \
    do { \
        hold >>= (n); \
        bits -= (unsigned)(n); \
    } while (0)

/* Remove zero to seven bits as needed to go to a byte boundary */
#define BYTEBITS() \
    do { \
        hold >>= bits & 7; \
        bits -= bits & 7; \
    } while (0)

#endif

/* Set mode=BAD and prepare error message */
#define SET_BAD(errmsg) \
    do { \
        state->mode = BAD; \
        strm->msg = (char *)errmsg; \
    } while (0)

/* Behave like chunkcopy, but avoid writing beyond of legal output. */
static inline uint8_t* chunkcopy_safe(uint8_t *out, uint8_t *from, uint64_t len, uint8_t *safe) {
    uint64_t safelen = (safe - out) + 1;
    len = MIN(len, safelen);
    int32_t olap_src = from >= out && from < out + len;
    int32_t olap_dst = out >= from && out < from + len;
    uint64_t tocopy;

    /* For all cases without overlap, memcpy is ideal */
    if (!(olap_src || olap_dst)) {
        memcpy(out, from, (size_t)len);
        return out + len;
    }

    /* We are emulating a self-modifying copy loop here. To do this in a way that doesn't produce undefined behavior,
     * we have to get a bit clever. First if the overlap is such that src falls between dst and dst+len, we can do the
     * initial bulk memcpy of the nonoverlapping region. Then, we can leverage the size of this to determine the safest
     * atomic memcpy size we can pick such that we have non-overlapping regions. This effectively becomes a safe look
     * behind or lookahead distance. */
    uint64_t non_olap_size = llabs(from - out); // llabs vs labs for compatibility with windows

    memcpy(out, from, (size_t)non_olap_size);
    out += non_olap_size;
    from += non_olap_size;
    len -= non_olap_size;

    /* So this doesn't give use a worst case scenario of function calls in a loop,
     * we want to instead break this down into copy blocks of fixed lengths */
    while (len) {
        tocopy = MIN(non_olap_size, len);
        len -= tocopy;

        while (tocopy >= 32) {
            memcpy(out, from, 32);
            out += 32;
            from += 32;
            tocopy -= 32;
        }

        if (tocopy >= 16) {
            memcpy(out, from, 16);
            out += 16;
            from += 16;
            tocopy -= 16;
        }

        if (tocopy >= 8) {
            zmemcpy_8(out, from);
            out += 8;
            from += 8;
            tocopy -= 8;
        }

        if (tocopy >= 4) {
            zmemcpy_4(out, from);
            out += 4;
            from += 4;
            tocopy -= 4;
        }

        if (tocopy >= 2) {
            zmemcpy_2(out, from);
            out += 2;
            from += 2;
            tocopy -= 2;
        }

        if (tocopy) {
            *out++ = *from++;
        }
    }

    return out;
}
