/* zmemory.h -- Private inline functions used internally in neozip
 * For conditions of distribution and use, see copyright notice in zlib.h
 */

#ifndef _ZMEMORY_H
#define _ZMEMORY_H

#if defined(__GNUC__) && (__GNUC__ >= 4)
#  define HAVE_MAY_ALIAS
#endif

static inline uint16_t zng_memread_2(const void *ptr) {
#if defined(HAVE_MAY_ALIAS)
    typedef struct { uint16_t val; } __attribute__ ((__packed__, __may_alias__)) unaligned_uint16_t;
    return ((const unaligned_uint16_t *)ptr)->val;
#else
    uint16_t val;
    memcpy(&val, ptr, sizeof(val));
    return val;
#endif
}

static inline uint32_t zng_memread_4(const void *ptr) {
#if defined(HAVE_MAY_ALIAS)
    typedef struct { uint32_t val; } __attribute__ ((__packed__, __may_alias__)) unaligned_uint32_t;
    return ((const unaligned_uint32_t *)ptr)->val;
#else
    uint32_t val;
    memcpy(&val, ptr, sizeof(val));
    return val;
#endif
}

static inline uint64_t zng_memread_8(const void *ptr) {
#if defined(HAVE_MAY_ALIAS)
    typedef struct { uint64_t val; } __attribute__ ((__packed__, __may_alias__)) unaligned_uint64_t;
    return ((const unaligned_uint64_t *)ptr)->val;
#else
    uint64_t val;
    memcpy(&val, ptr, sizeof(val));
    return val;
#endif
}

static inline void zng_memwrite_2(void *ptr, uint16_t val) {
#if defined(HAVE_MAY_ALIAS)
    typedef struct { uint16_t val; } __attribute__ ((__packed__, __may_alias__)) unaligned_uint16_t;
    ((unaligned_uint16_t *)ptr)->val = val;
#else
    memcpy(ptr, &val, sizeof(val));
#endif
}

static inline void zng_memwrite_4(void *ptr, uint32_t val) {
#if defined(HAVE_MAY_ALIAS)
    typedef struct { uint32_t val; } __attribute__ ((__packed__, __may_alias__)) unaligned_uint32_t;
    ((unaligned_uint32_t *)ptr)->val = val;
#else
    memcpy(ptr, &val, sizeof(val));
#endif
}

static inline void zng_memwrite_8(void *ptr, uint64_t val) {
#if defined(HAVE_MAY_ALIAS)
    typedef struct { uint64_t val; } __attribute__ ((__packed__, __may_alias__)) unaligned_uint64_t;
    ((unaligned_uint64_t *)ptr)->val = val;
#else
    memcpy(ptr, &val, sizeof(val));
#endif
}

/* Use zng_memread_* instead of memcmp to avoid older compilers not converting memcmp
   calls to unaligned comparisons when unaligned access is supported. Use memcmp only when
   unaligned support is not available to avoid an extra call to memcpy. */
static inline int32_t zng_memcmp_2(const void *src0, const void *src1) {
#if defined(HAVE_MAY_ALIAS)
    return zng_memread_2(src0) != zng_memread_2(src1);
#else
    return memcmp(src0, src1, 2);
#endif
}

static inline int32_t zng_memcmp_4(const void *src0, const void *src1) {
#if defined(HAVE_MAY_ALIAS)
    return zng_memread_4(src0) != zng_memread_4(src1);
#else
    return memcmp(src0, src1, 4);
#endif
}

static inline int32_t zng_memcmp_8(const void *src0, const void *src1) {
#if defined(HAVE_MAY_ALIAS)
    return zng_memread_8(src0) != zng_memread_8(src1);
#else
    return memcmp(src0, src1, 8);
#endif
}

#endif
