/*=========================================================================
 * Copyright (C) GemTalk Systems 2023-2024.  All Rights Reserved.
 *
 *   bit utilities header file
 *
 *========================================================================
 */

extern void gs_checkBitUtils(void);

/* Return whether the given (un)signed 64 bit integer is an (un)signed
 * integer of the given bit width.
 */
extern int sisks(int64_t, uint32_t);
extern int sisku(int64_t, uint32_t);
extern int uisks(uint64_t, uint32_t);
extern int uisku(uint64_t, uint32_t);

#define i64IsSigned(x, k) sisks(x, k)
#define i64IsUnsigned(x, k) sisku(x, k)
#define u64IsSigned(x, k) uisks(x, k)
#define u64IsUnsigned(x, k) uisku(x, k)

#define i64IsObjSize(x) sisku(x, OOP_NUM_MAX_BITS)
#define i64IsPositiveSmallInteger(x) sisku(x, 64 - OOP_NUM_TAG_BITS - 1)
#define u64IsPositiveSmallInteger(x) uisku(x, 64 - OOP_NUM_TAG_BITS - 1)

/* Return the index of the least significant bit set in the argument, starting
 * at index zero, or -1 if none.
 */
extern int  _gs_ffs(uint32_t);
extern int  _gs_ffs8(uint64_t);

/* Return the index of the most significant bit set in the argument, starting
 * at index zero, or 0 if none.
 */
extern int  _gs_msb(uint32_t);
extern int  _gs_msb8(uint64_t);

/* Return the number of bits set in the argument, or 0 if none */
extern int  _gs_popcnt8(uint64_t);

/* Swap endianness, casting arguments to the restricted size type first */
extern uint16_t _gs_bswap2(uint16_t);
extern uint32_t _gs_bswap4(uint32_t);
extern uint64_t _gs_bswap8(uint64_t);

/* Replace with builtins if available */
#if defined(FLG_LINUX_UNIX)

/* Use GCC's builtins */
#define GS_FFS_BUILTIN
#define gs_ffs(x) (__builtin_ffs(x) - 1)
#define gs_ffs8(x) (__builtin_ffsll(x) - 1)

#define GS_MSBIT_BUILTIN
#define gs_msb(x) ((x) == 0 ? 0 : 31 - __builtin_clz(x))
#define gs_msb8(x) ((x) == 0 ? 0 : 63 - __builtin_clzll(x))

#define GS_POPCNT_BUILTIN
#define gs_popcnt(x) __builtin_popcount(x)
#define gs_popcnt8(x) __builtin_popcountll(x)

#define gs_bswap2(x) __builtin_bswap16((uint16_t)(x))
#define gs_bswap4(x) __builtin_bswap32((uint32_t)(x))
#define gs_bswap8(x) __builtin_bswap64((uint64_t)(x))

#elif defined(FLG_AIX_UNIX)

/* Use XLC's builtins, but not for popcnt or bswap */
#define GS_FFS_BUILTIN
#define gs_ffs(x) ((x) == 0 ? -1 : __cnttz4(x))
#define gs_ffs8(x) ((x) == 0 ? -1LL : __cnttz8(x))

#define GS_MSBIT_BUILTIN
#define gs_msb(x) ((x) == 0 ? 0 : 31 - __cntlz4(x))
#define gs_msb8(x) ((x) == 0 ? 0 : 63 - __cntlz8(x))

#define gs_popcnt(x) _gs_popcnt8(x)
#define gs_popcnt8(x) _gs_popcnt8(x)
#define gs_bswap2(x) _gs_bswap2((uint16_t)(x))
#define gs_bswap4(x) _gs_bswap4((uint32_t)(x))
#define gs_bswap8(x) _gs_bswap8((uint64_t)(x))

#else

/* Use our functions */
#define gs_ffs(x) _gs_ffs(x)
#define gs_ffs8(x) _gs_ffs8(x)
#define gs_msb(x) _gs_msb(x)
#define gs_msb8(x) _gs_msb8(x)
#define gs_popcnt(x) _gs_popcnt8(x)
#define gs_popcnt8(x) _gs_popcnt8(x)
#define gs_bswap2(x) _gs_bswap2((uint16_t)(x))
#define gs_bswap4(x) _gs_bswap4((uint32_t)(x))
#define gs_bswap8(x) _gs_bswap8((uint64_t)(x))

#endif

/* For the sake of convenience using bswap */
#define gs_bswap4i(x) (int32_t)gs_bswap4(x)

#ifdef FLG_LITTLE_ENDIAN
#define gs_bswap2_ifle(x) gs_bswap2(x)
#define gs_bswap4_ifle(x) gs_bswap4(x)
#define gs_bswap8_ifle(x) gs_bswap8(x)
#else
#define gs_bswap2_ifle(x) (x)
#define gs_bswap4_ifle(x) (x)
#define gs_bswap8_ifle(x) (x)
#endif

