diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4f725e39..71c962f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,6 +53,7 @@ jobs: git config --global --add safe.directory '*' # needed when running in a container git clone --branch "$GITHUB_REF_NAME" --depth 1 "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" . git submodule update --init -- vendor/lzma-sdk + git fsck --strict --no-progress - name: Rebuild and verify stubs run: | export PATH="$(readlink -en ../deps/bin-upx-20221212):$PATH" diff --git a/src/bele.h b/src/bele.h index d6e35cce..e84e8825 100644 --- a/src/bele.h +++ b/src/bele.h @@ -32,14 +32,12 @@ // NE - Native Endianness (aka Host Endianness aka CPU Endianness) // TE - Target Endianness (not used here, see various packers) -#if 1 -// some platforms may provide their own system bswapXX() functions, so rename to avoid conflicts -#undef bswap16 -#undef bswap32 -#undef bswap64 -#define bswap16 upx_bswap16 -#define bswap32 upx_bswap32 -#define bswap64 upx_bswap64 +static_assert(std::is_same_v); + +#if defined(upx_is_constant_evaluated) +#define bele_constexpr constexpr +#else +#define bele_constexpr /*empty*/ #endif // forward declarations @@ -55,7 +53,7 @@ struct LE64; // try to detect XX16 vs XX32 vs XX64 size mismatches **************************************************************************/ -#if !(DEBUG) +#if !defined(upx_is_constant_evaluated) && !(DEBUG) // permissive version using "void *" #define REQUIRE_XE16 /*empty*/ @@ -109,67 +107,196 @@ using enable_if_xe64 = std::enable_if_t, T>; // core - NE **************************************************************************/ +forceinline bele_constexpr unsigned get_ne16(const byte *p) noexcept { +#if defined(upx_is_constant_evaluated) + if (upx_is_constant_evaluated()) { + typedef unsigned U; +#if (ACC_ABI_BIG_ENDIAN) + return (U(p[0]) << 8) | (U(p[1]) << 0); +#else + return (U(p[0]) << 0) | (U(p[1]) << 8); +#endif + } else +#endif + { + upx_uint16_t v = 0; + upx_memcpy_inline(&v, p, sizeof(v)); + return v; + } +} + +forceinline bele_constexpr unsigned get_ne32(const byte *p) noexcept { +#if defined(upx_is_constant_evaluated) + if (upx_is_constant_evaluated()) { + typedef unsigned U; +#if (ACC_ABI_BIG_ENDIAN) + return (U(p[0]) << 24) | (U(p[1]) << 16) | (U(p[2]) << 8) | (U(p[3]) << 0); +#else + return (U(p[0]) << 0) | (U(p[1]) << 8) | (U(p[2]) << 16) | (U(p[3]) << 24); +#endif + } else +#endif + { + upx_uint32_t v = 0; + upx_memcpy_inline(&v, p, sizeof(v)); + return v; + } +} + +forceinline bele_constexpr upx_uint64_t get_ne64(const byte *p) noexcept { +#if defined(upx_is_constant_evaluated) + if (upx_is_constant_evaluated()) { + typedef upx_uint64_t U; +#if (ACC_ABI_BIG_ENDIAN) + return (U(p[0]) << 56) | (U(p[1]) << 48) | (U(p[2]) << 40) | (U(p[3]) << 32) | + (U(p[4]) << 24) | (U(p[5]) << 16) | (U(p[6]) << 8) | (U(p[7]) << 0); +#else + return (U(p[0]) << 0) | (U(p[1]) << 8) | (U(p[2]) << 16) | (U(p[3]) << 24) | + (U(p[4]) << 32) | (U(p[5]) << 40) | (U(p[6]) << 48) | (U(p[7]) << 56); +#endif + } else +#endif + { + upx_uint64_t v = 0; + upx_memcpy_inline(&v, p, sizeof(v)); + return v; + } +} + +forceinline bele_constexpr void set_ne16(byte *p, unsigned v) noexcept { +#if defined(upx_is_constant_evaluated) + if (upx_is_constant_evaluated()) { +#if (ACC_ABI_BIG_ENDIAN) + p[0] = byte((v >> 8) & 0xff); + p[1] = byte((v >> 0) & 0xff); +#else + p[0] = byte((v >> 0) & 0xff); + p[1] = byte((v >> 8) & 0xff); +#endif + } else +#endif + { + upx_uint16_t vv = (upx_uint16_t) (v & 0xffff); + upx_memcpy_inline(p, &vv, sizeof(vv)); + } +} + +forceinline bele_constexpr void set_ne32(byte *p, unsigned v) noexcept { +#if defined(upx_is_constant_evaluated) + if (upx_is_constant_evaluated()) { +#if (ACC_ABI_BIG_ENDIAN) + p[0] = byte((v >> 24) & 0xff); + p[1] = byte((v >> 16) & 0xff); + p[2] = byte((v >> 8) & 0xff); + p[3] = byte((v >> 0) & 0xff); +#else + p[0] = byte((v >> 0) & 0xff); + p[1] = byte((v >> 8) & 0xff); + p[2] = byte((v >> 16) & 0xff); + p[3] = byte((v >> 24) & 0xff); +#endif + } else +#endif + { + upx_uint32_t vv = v; + upx_memcpy_inline(p, &vv, sizeof(vv)); + } +} + +forceinline bele_constexpr void set_ne64(byte *p, upx_uint64_t v) noexcept { +#if defined(upx_is_constant_evaluated) + if (upx_is_constant_evaluated()) { +#if (ACC_ABI_BIG_ENDIAN) + p[0] = byte((v >> 56) & 0xff); + p[1] = byte((v >> 48) & 0xff); + p[2] = byte((v >> 40) & 0xff); + p[3] = byte((v >> 32) & 0xff); + p[4] = byte((v >> 24) & 0xff); + p[5] = byte((v >> 16) & 0xff); + p[6] = byte((v >> 8) & 0xff); + p[7] = byte((v >> 0) & 0xff); +#else + p[0] = byte((v >> 0) & 0xff); + p[1] = byte((v >> 8) & 0xff); + p[2] = byte((v >> 16) & 0xff); + p[3] = byte((v >> 24) & 0xff); + p[4] = byte((v >> 32) & 0xff); + p[5] = byte((v >> 40) & 0xff); + p[6] = byte((v >> 48) & 0xff); + p[7] = byte((v >> 56) & 0xff); +#endif + } else +#endif + { + upx_uint64_t vv = v; + upx_memcpy_inline(p, &vv, sizeof(vv)); + } +} + +/************************************************************************* +// core - NE +**************************************************************************/ + REQUIRE_XE16 -static forceinline unsigned get_ne16(const XE16 *p) noexcept { - upx_uint16_t v = 0; - upx_memcpy_inline(&v, p, sizeof(v)); - return v; +forceinline bele_constexpr unsigned get_ne16(const XE16 *p) noexcept { + return get_ne16(upx::ptr_static_cast(p)); } REQUIRE_XE32 -static forceinline unsigned get_ne32(const XE32 *p) noexcept { - upx_uint32_t v = 0; - upx_memcpy_inline(&v, p, sizeof(v)); - return v; +forceinline bele_constexpr unsigned get_ne32(const XE32 *p) noexcept { + return get_ne32(upx::ptr_static_cast(p)); } REQUIRE_XE64 -static forceinline upx_uint64_t get_ne64(const XE64 *p) noexcept { - upx_uint64_t v = 0; - upx_memcpy_inline(&v, p, sizeof(v)); - return v; +forceinline bele_constexpr upx_uint64_t get_ne64(const XE64 *p) noexcept { + return get_ne64(upx::ptr_static_cast(p)); } REQUIRE_XE16 -static forceinline void set_ne16(XE16 *p, unsigned vv) noexcept { - upx_uint16_t v = (upx_uint16_t) (vv & 0xffff); - upx_memcpy_inline(p, &v, sizeof(v)); +forceinline bele_constexpr void set_ne16(XE16 *p, unsigned v) noexcept { + set_ne16(upx::ptr_static_cast(p), v); } REQUIRE_XE32 -static forceinline void set_ne32(XE32 *p, unsigned vv) noexcept { - upx_uint32_t v = vv; - upx_memcpy_inline(p, &v, sizeof(v)); +forceinline bele_constexpr void set_ne32(XE32 *p, unsigned v) noexcept { + set_ne32(upx::ptr_static_cast(p), v); } REQUIRE_XE64 -static forceinline void set_ne64(XE64 *p, upx_uint64_t vv) noexcept { - upx_uint64_t v = vv; - upx_memcpy_inline(p, &v, sizeof(v)); +forceinline bele_constexpr void set_ne64(XE64 *p, upx_uint64_t v) noexcept { + set_ne64(upx::ptr_static_cast(p), v); } /************************************************************************* // core - bswap **************************************************************************/ -#if (ACC_CC_MSC) +#if __cpp_lib_byteswap >= 202110L + +forceinline constexpr unsigned bswap16(unsigned v) noexcept { + return std::byteswap((upx_uint16_t) (v & 0xffff)); +} +forceinline constexpr unsigned bswap32(unsigned v) noexcept { return std::byteswap(v); } +forceinline constexpr upx_uint64_t bswap64(upx_uint64_t v) noexcept { return std::byteswap(v); } + +#elif (ACC_CC_MSC) ACC_COMPILE_TIME_ASSERT_HEADER(sizeof(long) == 4) // unfortunately *not* constexpr with current MSVC -static forceinline /*constexpr*/ unsigned bswap16(unsigned v) noexcept { +forceinline /*constexpr*/ unsigned bswap16(unsigned v) noexcept { return (unsigned) _byteswap_ulong(v << 16); } -static forceinline /*constexpr*/ unsigned bswap32(unsigned v) noexcept { +forceinline /*constexpr*/ unsigned bswap32(unsigned v) noexcept { return (unsigned) _byteswap_ulong(v); } -static forceinline /*constexpr*/ upx_uint64_t bswap64(upx_uint64_t v) noexcept { +forceinline /*constexpr*/ upx_uint64_t bswap64(upx_uint64_t v) noexcept { return _byteswap_uint64(v); } #else -static forceinline constexpr unsigned bswap16(unsigned v) noexcept { +forceinline constexpr unsigned bswap16(unsigned v) noexcept { #if defined(__riscv) && __riscv_xlen == 64 return (unsigned) __builtin_bswap64((upx_uint64_t) v << 48); #else @@ -177,25 +304,23 @@ static forceinline constexpr unsigned bswap16(unsigned v) noexcept { return __builtin_bswap32(v << 16); #endif } -static forceinline constexpr unsigned bswap32(unsigned v) noexcept { +forceinline constexpr unsigned bswap32(unsigned v) noexcept { #if defined(__riscv) && __riscv_xlen == 64 return (unsigned) __builtin_bswap64((upx_uint64_t) v << 32); #else return __builtin_bswap32(v); #endif } -static forceinline constexpr upx_uint64_t bswap64(upx_uint64_t v) noexcept { - return __builtin_bswap64(v); -} +forceinline constexpr upx_uint64_t bswap64(upx_uint64_t v) noexcept { return __builtin_bswap64(v); } #endif -static forceinline constexpr unsigned no_bswap16(unsigned v) noexcept { +forceinline constexpr unsigned no_bswap16(unsigned v) noexcept { // mask is needed so that for all v: bswap16(bswap16(v)) == no_bswap16(v) return v & 0xffff; } -static forceinline constexpr unsigned no_bswap32(unsigned v) noexcept { return v; } -static forceinline constexpr upx_uint64_t no_bswap64(upx_uint64_t v) noexcept { return v; } +forceinline constexpr unsigned no_bswap32(unsigned v) noexcept { return v; } +forceinline constexpr upx_uint64_t no_bswap64(upx_uint64_t v) noexcept { return v; } #if (ACC_ABI_BIG_ENDIAN) #define ne16_to_be16(v) no_bswap16(v) @@ -218,72 +343,106 @@ static forceinline constexpr upx_uint64_t no_bswap64(upx_uint64_t v) noexcept { **************************************************************************/ REQUIRE_XE16 -inline unsigned get_be16(const XE16 *p) noexcept { return ne16_to_be16(get_ne16(p)); } +inline bele_constexpr unsigned get_be16(const XE16 *p) noexcept { + return ne16_to_be16(get_ne16(p)); +} REQUIRE_XE32 -inline unsigned get_be32(const XE32 *p) noexcept { return ne32_to_be32(get_ne32(p)); } +inline bele_constexpr unsigned get_be32(const XE32 *p) noexcept { + return ne32_to_be32(get_ne32(p)); +} REQUIRE_XE64 -inline upx_uint64_t get_be64(const XE64 *p) noexcept { return ne64_to_be64(get_ne64(p)); } +inline bele_constexpr upx_uint64_t get_be64(const XE64 *p) noexcept { + return ne64_to_be64(get_ne64(p)); +} REQUIRE_XE16 -inline unsigned get_le16(const XE16 *p) noexcept { return ne16_to_le16(get_ne16(p)); } +inline bele_constexpr unsigned get_le16(const XE16 *p) noexcept { + return ne16_to_le16(get_ne16(p)); +} REQUIRE_XE32 -inline unsigned get_le32(const XE32 *p) noexcept { return ne32_to_le32(get_ne32(p)); } +inline bele_constexpr unsigned get_le32(const XE32 *p) noexcept { + return ne32_to_le32(get_ne32(p)); +} REQUIRE_XE64 -inline upx_uint64_t get_le64(const XE64 *p) noexcept { return ne64_to_le64(get_ne64(p)); } +inline bele_constexpr upx_uint64_t get_le64(const XE64 *p) noexcept { + return ne64_to_le64(get_ne64(p)); +} REQUIRE_XE16 -inline void set_be16(XE16 *p, unsigned v) noexcept { set_ne16(p, ne16_to_be16(v)); } +inline bele_constexpr void set_be16(XE16 *p, unsigned v) noexcept { set_ne16(p, ne16_to_be16(v)); } REQUIRE_XE32 -inline void set_be32(XE32 *p, unsigned v) noexcept { set_ne32(p, ne32_to_be32(v)); } +inline bele_constexpr void set_be32(XE32 *p, unsigned v) noexcept { set_ne32(p, ne32_to_be32(v)); } REQUIRE_XE64 -inline void set_be64(XE64 *p, upx_uint64_t v) noexcept { set_ne64(p, ne64_to_be64(v)); } +inline bele_constexpr void set_be64(XE64 *p, upx_uint64_t v) noexcept { + set_ne64(p, ne64_to_be64(v)); +} REQUIRE_XE16 -inline void set_le16(XE16 *p, unsigned v) noexcept { set_ne16(p, ne16_to_le16(v)); } +inline bele_constexpr void set_le16(XE16 *p, unsigned v) noexcept { set_ne16(p, ne16_to_le16(v)); } REQUIRE_XE32 -inline void set_le32(XE32 *p, unsigned v) noexcept { set_ne32(p, ne32_to_le32(v)); } +inline bele_constexpr void set_le32(XE32 *p, unsigned v) noexcept { set_ne32(p, ne32_to_le32(v)); } REQUIRE_XE64 -inline void set_le64(XE64 *p, upx_uint64_t v) noexcept { set_ne64(p, ne64_to_le64(v)); } +inline bele_constexpr void set_le64(XE64 *p, upx_uint64_t v) noexcept { + set_ne64(p, ne64_to_le64(v)); +} /************************************************************************* // get/set 24/26 **************************************************************************/ -REQUIRE_XE24 -inline unsigned get_be24(const XE24 *p) noexcept { - const byte *b = ACC_CCAST(const byte *, p); - return (b[0] << 16) | (b[1] << 8) | (b[2] << 0); +inline constexpr unsigned get_be24(const byte *p) noexcept { + typedef unsigned U; + return (U(p[0]) << 16) | (U(p[1]) << 8) | (U(p[2]) << 0); +} + +inline constexpr unsigned get_le24(const byte *p) noexcept { + typedef unsigned U; + return (U(p[0]) << 0) | (U(p[1]) << 8) | (U(p[2]) << 16); +} + +inline constexpr void set_be24(byte *p, unsigned v) noexcept { + p[0] = byte((v >> 16) & 0xff); + p[1] = byte((v >> 8) & 0xff); + p[2] = byte((v >> 0) & 0xff); +} + +inline constexpr void set_le24(byte *p, unsigned v) noexcept { + p[0] = byte((v >> 0) & 0xff); + p[1] = byte((v >> 8) & 0xff); + p[2] = byte((v >> 16) & 0xff); } REQUIRE_XE24 -inline unsigned get_le24(const XE24 *p) noexcept { - const byte *b = ACC_CCAST(const byte *, p); - return (b[0] << 0) | (b[1] << 8) | (b[2] << 16); +forceinline bele_constexpr unsigned get_be24(const XE24 *p) noexcept { + return get_be24(upx::ptr_static_cast(p)); } REQUIRE_XE24 -inline void set_be24(XE24 *p, unsigned v) noexcept { - byte *b = ACC_PCAST(byte *, p); - b[0] = ACC_ICONV(byte, (v >> 16) & 0xff); - b[1] = ACC_ICONV(byte, (v >> 8) & 0xff); - b[2] = ACC_ICONV(byte, (v >> 0) & 0xff); +forceinline bele_constexpr unsigned get_le24(const XE24 *p) noexcept { + return get_le24(upx::ptr_static_cast(p)); } REQUIRE_XE24 -inline void set_le24(XE24 *p, unsigned v) noexcept { - byte *b = ACC_PCAST(byte *, p); - b[0] = ACC_ICONV(byte, (v >> 0) & 0xff); - b[1] = ACC_ICONV(byte, (v >> 8) & 0xff); - b[2] = ACC_ICONV(byte, (v >> 16) & 0xff); +forceinline bele_constexpr void set_be24(XE24 *p, unsigned v) noexcept { + set_be24(upx::ptr_static_cast(p), v); +} + +REQUIRE_XE24 +forceinline bele_constexpr void set_le24(XE24 *p, unsigned v) noexcept { + set_le24(upx::ptr_static_cast(p), v); } REQUIRE_XE32 -inline unsigned get_le26(const XE32 *p) noexcept { return get_le32(p) & 0x03ffffff; } +inline bele_constexpr unsigned get_le26(const XE32 *p) noexcept { return get_le32(p) & 0x03ffffff; } REQUIRE_XE32 -inline unsigned get_le19_5(const XE32 *p) noexcept { return (get_le32(p) >> 5) & 0x0007ffff; } +inline bele_constexpr unsigned get_le19_5(const XE32 *p) noexcept { + return (get_le32(p) >> 5) & 0x0007ffff; +} REQUIRE_XE32 -inline unsigned get_le14_5(const XE32 *p) noexcept { return (get_le32(p) >> 5) & 0x00003fff; } +inline bele_constexpr unsigned get_le14_5(const XE32 *p) noexcept { + return (get_le32(p) >> 5) & 0x00003fff; +} REQUIRE_XE32 -inline void set_le26(XE32 *p, unsigned v) noexcept { +inline bele_constexpr void set_le26(XE32 *p, unsigned v) noexcept { // preserve the top 6 bits // set_le32(p, (get_le32(p) & 0xfc000000) | (v & 0x03ffffff)); // optimized version, saving a runtime bswap32 @@ -291,11 +450,11 @@ inline void set_le26(XE32 *p, unsigned v) noexcept { (ne32_to_le32(v) & ne32_to_le32(0x03ffffff))); } REQUIRE_XE32 -inline void set_le19_5(XE32 *p, unsigned v) noexcept { +inline bele_constexpr void set_le19_5(XE32 *p, unsigned v) noexcept { set_le32(p, (get_le32(p) & 0xff00001f) | ((v & 0x0007ffff) << 5)); } REQUIRE_XE32 -inline void set_le14_5(XE32 *p, unsigned v) noexcept { +inline bele_constexpr void set_le14_5(XE32 *p, unsigned v) noexcept { set_le32(p, (get_le32(p) & 0xfff8001f) | ((v & 0x00003fff) << 5)); } @@ -303,7 +462,7 @@ inline void set_le14_5(XE32 *p, unsigned v) noexcept { // get signed values **************************************************************************/ -static forceinline constexpr int sign_extend(unsigned v, unsigned bits) noexcept { +forceinline constexpr int sign_extend(unsigned v, unsigned bits) noexcept { #if (ACC_ARCH_M68K) // no barrel shifter const unsigned sign_bit = 1u << (bits - 1); return ACC_ICAST(int, (v & (sign_bit - 1)) - (v & sign_bit)); @@ -312,7 +471,7 @@ static forceinline constexpr int sign_extend(unsigned v, unsigned bits) noexcept #endif } -static forceinline constexpr upx_int64_t sign_extend(upx_uint64_t v, unsigned bits) noexcept { +forceinline constexpr upx_int64_t sign_extend(upx_uint64_t v, unsigned bits) noexcept { #if (ACC_ARCH_M68K) // no barrel shifter const upx_uint64_t sign_bit = 1ull << (bits - 1); return ACC_ICAST(upx_int64_t, (v & (sign_bit - 1)) - (v & sign_bit)); @@ -322,49 +481,49 @@ static forceinline constexpr upx_int64_t sign_extend(upx_uint64_t v, unsigned bi } REQUIRE_XE16 -inline int get_be16_signed(const XE16 *p) noexcept { +inline bele_constexpr int get_be16_signed(const XE16 *p) noexcept { unsigned v = get_be16(p); return sign_extend(v, 16); } REQUIRE_XE24 -inline int get_be24_signed(const XE24 *p) noexcept { +inline bele_constexpr int get_be24_signed(const XE24 *p) noexcept { unsigned v = get_be24(p); return sign_extend(v, 24); } REQUIRE_XE32 -inline int get_be32_signed(const XE32 *p) noexcept { +inline bele_constexpr int get_be32_signed(const XE32 *p) noexcept { unsigned v = get_be32(p); return sign_extend(v, 32); } REQUIRE_XE64 -inline upx_int64_t get_be64_signed(const XE64 *p) noexcept { +inline bele_constexpr upx_int64_t get_be64_signed(const XE64 *p) noexcept { upx_uint64_t v = get_be64(p); return sign_extend(v, 64); } REQUIRE_XE16 -inline int get_le16_signed(const XE16 *p) noexcept { +inline bele_constexpr int get_le16_signed(const XE16 *p) noexcept { unsigned v = get_le16(p); return sign_extend(v, 16); } REQUIRE_XE24 -inline int get_le24_signed(const XE24 *p) noexcept { +inline bele_constexpr int get_le24_signed(const XE24 *p) noexcept { unsigned v = get_le24(p); return sign_extend(v, 24); } REQUIRE_XE32 -inline int get_le32_signed(const XE32 *p) noexcept { +inline bele_constexpr int get_le32_signed(const XE32 *p) noexcept { unsigned v = get_le32(p); return sign_extend(v, 32); } REQUIRE_XE64 -inline upx_int64_t get_le64_signed(const XE64 *p) noexcept { +inline bele_constexpr upx_int64_t get_le64_signed(const XE64 *p) noexcept { upx_uint64_t v = get_le64(p); return sign_extend(v, 64); } @@ -384,9 +543,16 @@ struct alignas(1) BE16 final { typedef unsigned integral_conversion_type; // automatic conversion to unsigned byte d[2]; - forceinline operator unsigned() const noexcept { return get_be16(d); } + static forceinline constexpr BE16 make(const BE16 &x) noexcept { return x; } + static forceinline bele_constexpr BE16 make(unsigned v) noexcept { + BE16 x = {}; + set_be16(x.d, v); + return x; + } - forceinline BE16 &operator=(unsigned v) noexcept { + forceinline bele_constexpr operator unsigned() const noexcept { return get_be16(d); } + + forceinline bele_constexpr BE16 &operator=(unsigned v) noexcept { set_be16(d, v); return *this; } @@ -427,17 +593,28 @@ struct alignas(1) BE16 final { return *this; } - bool operator==(const BE16 &x) const noexcept { return memcmp(d, x.d, sizeof(d)) == 0; } - bool operator<(const BE16 &x) const noexcept { return unsigned(*this) < unsigned(x); } + bele_constexpr bool operator==(const BE16 &x) const noexcept { + return upx_memcmp_inline(d, x.d, sizeof(d)) == 0; + } + bele_constexpr bool operator<(const BE16 &x) const noexcept { + return unsigned(*this) < unsigned(x); + } }; struct alignas(1) BE32 final { typedef unsigned integral_conversion_type; // automatic conversion to unsigned byte d[4]; - forceinline operator unsigned() const noexcept { return get_be32(d); } + static forceinline constexpr BE32 make(const BE32 &x) noexcept { return x; } + static forceinline bele_constexpr BE32 make(unsigned v) noexcept { + BE32 x = {}; + set_be32(x.d, v); + return x; + } - forceinline BE32 &operator=(unsigned v) noexcept { + forceinline bele_constexpr operator unsigned() const noexcept { return get_be32(d); } + + forceinline bele_constexpr BE32 &operator=(unsigned v) noexcept { set_be32(d, v); return *this; } @@ -478,17 +655,28 @@ struct alignas(1) BE32 final { return *this; } - bool operator==(const BE32 &x) const noexcept { return memcmp(d, x.d, sizeof(d)) == 0; } - bool operator<(const BE32 &x) const noexcept { return unsigned(*this) < unsigned(x); } + bele_constexpr bool operator==(const BE32 &x) const noexcept { + return upx_memcmp_inline(d, x.d, sizeof(d)) == 0; + } + bele_constexpr bool operator<(const BE32 &x) const noexcept { + return unsigned(*this) < unsigned(x); + } }; struct alignas(1) BE64 final { typedef upx_uint64_t integral_conversion_type; // automatic conversion to upx_uint64_t byte d[8]; - forceinline operator upx_uint64_t() const noexcept { return get_be64(d); } + static forceinline constexpr BE64 make(const BE64 &x) noexcept { return x; } + static forceinline bele_constexpr BE64 make(upx_uint64_t v) noexcept { + BE64 x = {}; + set_be64(x.d, v); + return x; + } - forceinline BE64 &operator=(upx_uint64_t v) noexcept { + forceinline bele_constexpr operator upx_uint64_t() const noexcept { return get_be64(d); } + + forceinline bele_constexpr BE64 &operator=(upx_uint64_t v) noexcept { set_be64(d, v); return *this; } @@ -529,17 +717,28 @@ struct alignas(1) BE64 final { return *this; } - bool operator==(const BE64 &x) const noexcept { return memcmp(d, x.d, sizeof(d)) == 0; } - bool operator<(const BE64 &x) const noexcept { return upx_uint64_t(*this) < upx_uint64_t(x); } + bele_constexpr bool operator==(const BE64 &x) const noexcept { + return upx_memcmp_inline(d, x.d, sizeof(d)) == 0; + } + bele_constexpr bool operator<(const BE64 &x) const noexcept { + return upx_uint64_t(*this) < upx_uint64_t(x); + } }; struct alignas(1) LE16 final { typedef unsigned integral_conversion_type; // automatic conversion to unsigned byte d[2]; - forceinline operator unsigned() const noexcept { return get_le16(d); } + static forceinline constexpr LE16 make(const LE16 &x) noexcept { return x; } + static forceinline bele_constexpr LE16 make(unsigned v) noexcept { + LE16 x = {}; + set_le16(x.d, v); + return x; + } - forceinline LE16 &operator=(unsigned v) noexcept { + forceinline bele_constexpr operator unsigned() const noexcept { return get_le16(d); } + + forceinline bele_constexpr LE16 &operator=(unsigned v) noexcept { set_le16(d, v); return *this; } @@ -580,17 +779,28 @@ struct alignas(1) LE16 final { return *this; } - bool operator==(const LE16 &x) const noexcept { return memcmp(d, x.d, sizeof(d)) == 0; } - bool operator<(const LE16 &x) const noexcept { return unsigned(*this) < unsigned(x); } + bele_constexpr bool operator==(const LE16 &x) const noexcept { + return upx_memcmp_inline(d, x.d, sizeof(d)) == 0; + } + bele_constexpr bool operator<(const LE16 &x) const noexcept { + return unsigned(*this) < unsigned(x); + } }; struct alignas(1) LE32 final { typedef unsigned integral_conversion_type; // automatic conversion to unsigned byte d[4]; - forceinline operator unsigned() const noexcept { return get_le32(d); } + static forceinline constexpr LE32 make(const LE32 &x) noexcept { return x; } + static forceinline bele_constexpr LE32 make(unsigned v) noexcept { + LE32 x = {}; + set_le32(x.d, v); + return x; + } - forceinline LE32 &operator=(unsigned v) noexcept { + forceinline bele_constexpr operator unsigned() const noexcept { return get_le32(d); } + + forceinline bele_constexpr LE32 &operator=(unsigned v) noexcept { set_le32(d, v); return *this; } @@ -631,17 +841,28 @@ struct alignas(1) LE32 final { return *this; } - bool operator==(const LE32 &x) const noexcept { return memcmp(d, x.d, sizeof(d)) == 0; } - bool operator<(const LE32 &x) const noexcept { return unsigned(*this) < unsigned(x); } + bele_constexpr bool operator==(const LE32 &x) const noexcept { + return upx_memcmp_inline(d, x.d, sizeof(d)) == 0; + } + bele_constexpr bool operator<(const LE32 &x) const noexcept { + return unsigned(*this) < unsigned(x); + } }; struct alignas(1) LE64 final { typedef upx_uint64_t integral_conversion_type; // automatic conversion to upx_uint64_t byte d[8]; - forceinline operator upx_uint64_t() const noexcept { return get_le64(d); } + static forceinline constexpr LE64 make(const LE64 &x) noexcept { return x; } + static forceinline bele_constexpr LE64 make(upx_uint64_t v) noexcept { + LE64 x = {}; + set_le64(x.d, v); + return x; + } - forceinline LE64 &operator=(upx_uint64_t v) noexcept { + forceinline bele_constexpr operator upx_uint64_t() const noexcept { return get_le64(d); } + + forceinline bele_constexpr LE64 &operator=(upx_uint64_t v) noexcept { set_le64(d, v); return *this; } @@ -682,8 +903,12 @@ struct alignas(1) LE64 final { return *this; } - bool operator==(const LE64 &x) const noexcept { return memcmp(d, x.d, sizeof(d)) == 0; } - bool operator<(const LE64 &x) const noexcept { return upx_uint64_t(*this) < upx_uint64_t(x); } + bele_constexpr bool operator==(const LE64 &x) const noexcept { + return upx_memcmp_inline(d, x.d, sizeof(d)) == 0; + } + bele_constexpr bool operator<(const LE64 &x) const noexcept { + return upx_uint64_t(*this) < upx_uint64_t(x); + } }; /************************************************************************* @@ -691,35 +916,35 @@ struct alignas(1) LE64 final { **************************************************************************/ template -inline T *operator+(T *ptr, const BE16 &v) noexcept { +inline bele_constexpr T *operator+(T *ptr, const BE16 &v) noexcept { return ptr + unsigned(v); } template -inline T *operator-(T *ptr, const BE16 &v) noexcept { +inline bele_constexpr T *operator-(T *ptr, const BE16 &v) noexcept { return ptr - unsigned(v); } template -inline T *operator+(T *ptr, const BE32 &v) noexcept { +inline bele_constexpr T *operator+(T *ptr, const BE32 &v) noexcept { return ptr + unsigned(v); } template -inline T *operator-(T *ptr, const BE32 &v) noexcept { +inline bele_constexpr T *operator-(T *ptr, const BE32 &v) noexcept { return ptr - unsigned(v); } template -inline T *operator+(T *ptr, const LE16 &v) noexcept { +inline bele_constexpr T *operator+(T *ptr, const LE16 &v) noexcept { return ptr + unsigned(v); } template -inline T *operator-(T *ptr, const LE16 &v) noexcept { +inline bele_constexpr T *operator-(T *ptr, const LE16 &v) noexcept { return ptr - unsigned(v); } template -inline T *operator+(T *ptr, const LE32 &v) noexcept { +inline bele_constexpr T *operator+(T *ptr, const LE32 &v) noexcept { return ptr + unsigned(v); } template -inline T *operator-(T *ptr, const LE32 &v) noexcept { +inline bele_constexpr T *operator-(T *ptr, const LE32 &v) noexcept { return ptr - unsigned(v); } @@ -761,72 +986,80 @@ namespace upx { template , T> > REQUIRE_UINT32 -inline T align_down(const T &a, const BE32 &b) noexcept { return align_down(a, T(b)); } +inline bele_constexpr T align_down(const T &a, const BE32 &b) noexcept { + return align_down(a, T(b)); +} REQUIRE_UINT32 -inline T align_down(const BE32 &a, const T &b) noexcept { return align_down(T(a), b); } +inline bele_constexpr T align_down(const BE32 &a, const T &b) noexcept { + return align_down(T(a), b); +} REQUIRE_UINT32 -inline T align_down(const T &a, const LE32 &b) noexcept { return align_down(a, T(b)); } +inline bele_constexpr T align_down(const T &a, const LE32 &b) noexcept { + return align_down(a, T(b)); +} REQUIRE_UINT32 -inline T align_down(const LE32 &a, const T &b) noexcept { return align_down(T(a), b); } +inline bele_constexpr T align_down(const LE32 &a, const T &b) noexcept { + return align_down(T(a), b); +} REQUIRE_UINT32 -inline T align_up(const T &a, const LE32 &b) noexcept { return align_up(a, T(b)); } +inline bele_constexpr T align_up(const T &a, const LE32 &b) noexcept { return align_up(a, T(b)); } REQUIRE_UINT32 -inline T align_up(const LE32 &a, const T &b) noexcept { return align_up(T(a), b); } +inline bele_constexpr T align_up(const LE32 &a, const T &b) noexcept { return align_up(T(a), b); } REQUIRE_UINT32 -inline T align_up(const T &a, const BE32 &b) noexcept { return align_up(a, T(b)); } +inline bele_constexpr T align_up(const T &a, const BE32 &b) noexcept { return align_up(a, T(b)); } REQUIRE_UINT32 -inline T align_up(const BE32 &a, const T &b) noexcept { return align_up(T(a), b); } +inline bele_constexpr T align_up(const BE32 &a, const T &b) noexcept { return align_up(T(a), b); } REQUIRE_UINT32 -inline T min(const T &a, const BE16 &b) noexcept { return min(a, T(b)); } +inline bele_constexpr T min(const T &a, const BE16 &b) noexcept { return min(a, T(b)); } REQUIRE_UINT32 -inline T min(const BE16 &a, const T &b) noexcept { return min(T(a), b); } +inline bele_constexpr T min(const BE16 &a, const T &b) noexcept { return min(T(a), b); } REQUIRE_UINT32 -inline T min(const T &a, const BE32 &b) noexcept { return min(a, T(b)); } +inline bele_constexpr T min(const T &a, const BE32 &b) noexcept { return min(a, T(b)); } REQUIRE_UINT32 -inline T min(const BE32 &a, const T &b) noexcept { return min(T(a), b); } +inline bele_constexpr T min(const BE32 &a, const T &b) noexcept { return min(T(a), b); } REQUIRE_UINT64 -inline T min(const T &a, const BE64 &b) noexcept { return min(a, T(b)); } +inline bele_constexpr T min(const T &a, const BE64 &b) noexcept { return min(a, T(b)); } REQUIRE_UINT64 -inline T min(const BE64 &a, const T &b) noexcept { return min(T(a), b); } +inline bele_constexpr T min(const BE64 &a, const T &b) noexcept { return min(T(a), b); } REQUIRE_UINT32 -inline T min(const T &a, const LE16 &b) noexcept { return min(a, T(b)); } +inline bele_constexpr T min(const T &a, const LE16 &b) noexcept { return min(a, T(b)); } REQUIRE_UINT32 -inline T min(const LE16 &a, const T &b) noexcept { return min(T(a), b); } +inline bele_constexpr T min(const LE16 &a, const T &b) noexcept { return min(T(a), b); } REQUIRE_UINT32 -inline T min(const T &a, const LE32 &b) noexcept { return min(a, T(b)); } +inline bele_constexpr T min(const T &a, const LE32 &b) noexcept { return min(a, T(b)); } REQUIRE_UINT32 -inline T min(const LE32 &a, const T &b) noexcept { return min(T(a), b); } +inline bele_constexpr T min(const LE32 &a, const T &b) noexcept { return min(T(a), b); } REQUIRE_UINT64 -inline T min(const T &a, const LE64 &b) noexcept { return min(a, T(b)); } +inline bele_constexpr T min(const T &a, const LE64 &b) noexcept { return min(a, T(b)); } REQUIRE_UINT64 -inline T min(const LE64 &a, const T &b) noexcept { return min(T(a), b); } +inline bele_constexpr T min(const LE64 &a, const T &b) noexcept { return min(T(a), b); } REQUIRE_UINT32 -inline T max(const T &a, const BE16 &b) noexcept { return max(a, T(b)); } +inline bele_constexpr T max(const T &a, const BE16 &b) noexcept { return max(a, T(b)); } REQUIRE_UINT32 -inline T max(const BE16 &a, const T &b) noexcept { return max(T(a), b); } +inline bele_constexpr T max(const BE16 &a, const T &b) noexcept { return max(T(a), b); } REQUIRE_UINT32 -inline T max(const T &a, const BE32 &b) noexcept { return max(a, T(b)); } +inline bele_constexpr T max(const T &a, const BE32 &b) noexcept { return max(a, T(b)); } REQUIRE_UINT32 -inline T max(const BE32 &a, const T &b) noexcept { return max(T(a), b); } +inline bele_constexpr T max(const BE32 &a, const T &b) noexcept { return max(T(a), b); } REQUIRE_UINT64 -inline T max(const T &a, const BE64 &b) noexcept { return max(a, T(b)); } +inline bele_constexpr T max(const T &a, const BE64 &b) noexcept { return max(a, T(b)); } REQUIRE_UINT64 -inline T max(const BE64 &a, const T &b) noexcept { return max(T(a), b); } +inline bele_constexpr T max(const BE64 &a, const T &b) noexcept { return max(T(a), b); } REQUIRE_UINT32 -inline T max(const T &a, const LE16 &b) noexcept { return max(a, T(b)); } +inline bele_constexpr T max(const T &a, const LE16 &b) noexcept { return max(a, T(b)); } REQUIRE_UINT32 -inline T max(const LE16 &a, const T &b) noexcept { return max(T(a), b); } +inline bele_constexpr T max(const LE16 &a, const T &b) noexcept { return max(T(a), b); } REQUIRE_UINT32 -inline T max(const T &a, const LE32 &b) noexcept { return max(a, T(b)); } +inline bele_constexpr T max(const T &a, const LE32 &b) noexcept { return max(a, T(b)); } REQUIRE_UINT32 -inline T max(const LE32 &a, const T &b) noexcept { return max(T(a), b); } +inline bele_constexpr T max(const LE32 &a, const T &b) noexcept { return max(T(a), b); } REQUIRE_UINT64 -inline T max(const T &a, const LE64 &b) noexcept { return max(a, T(b)); } +inline bele_constexpr T max(const T &a, const LE64 &b) noexcept { return max(a, T(b)); } REQUIRE_UINT64 -inline T max(const LE64 &a, const T &b) noexcept { return max(T(a), b); } +inline bele_constexpr T max(const LE64 &a, const T &b) noexcept { return max(T(a), b); } #undef REQUIRE_UINT32 #undef REQUIRE_UINT64 diff --git a/src/bele_policy.h b/src/bele_policy.h index f03f8d07..f125bf44 100644 --- a/src/bele_policy.h +++ b/src/bele_policy.h @@ -33,7 +33,7 @@ #if defined(BELE_CTP) // CTP - Compile-Time Polymorphism (templates) -#define V static inline +#define V static inline bele_constexpr #define VV static forceinline_constexpr #define S static int __acc_cdecl_qsort #define C noexcept diff --git a/src/check/dt_check.cpp b/src/check/dt_check.cpp index c0d53f72..4247fbda 100644 --- a/src/check/dt_check.cpp +++ b/src/check/dt_check.cpp @@ -236,6 +236,8 @@ struct CheckIntegral { one = 1; three = 3; four = 4; + assert_noexcept(one == 1); + assert_noexcept(four == 4); // min / max assert_noexcept(upx::min(one, four) == 1); assert_noexcept(upx::min(one, four) == one); @@ -411,6 +413,44 @@ struct TestBELE { assert_noexcept(upx::max(minus_one_t, minus_two_u) == minus_one_t); assert_noexcept(upx::max(minus_one_t, minus_two_u) == minus_one_u); } + // constexpr + { + constexpr T zero = {}; + constexpr T zero_copy = T::make(zero); + assert_noexcept(zero_copy == 0); + } +#if defined(upx_is_constant_evaluated) + { + typedef typename T::integral_conversion_type U; + constexpr T one = T::make(1); + static_assert(one == 1); + constexpr T four = T::make(one + 3); + static_assert(four == 4); + constexpr U all_bits_u = (U) T::make(U(0) - U(1)); + constexpr T all_bits = T::make(all_bits_u); + static_assert(all_bits == all_bits_u); + static_assert(all_bits == T::make(one - 2)); + static_assert(one == one); + static_assert(!(one == four)); + static_assert(!(one == all_bits)); + static_assert(one < four); + static_assert(one < all_bits); + static_assert(upx::min(one, four) == 1); + static_assert(upx::min(one, four) == one); + static_assert(upx::min(U(1), four) == 1); + static_assert(upx::min(one, U(4)) == 1); + static_assert(upx::max(one, four) == 4); + static_assert(upx::max(one, four) == four); + static_assert(upx::max(U(1), four) == 4); + static_assert(upx::max(one, U(4)) == 4); + static_assert(upx::align_down(one, four) == 0); + static_assert(upx::align_up(one, four) == 4); + static_assert(upx::align_gap(one, four) == 3); + constexpr T one_copy = T::make(one); + static_assert(one_copy == one); + static_assert(one_copy == 1); + } +#endif return true; } }; @@ -690,12 +730,11 @@ void upx_compiler_sanity_check(void) noexcept { assert_noexcept(TestBELE::test()); assert_noexcept(TestBELE::test()); { - alignas(16) static const byte dd[32] = { + alignas(16) static constexpr byte dd[32] = { 0, 0, 0, 0, 0, 0, 0, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0, 0, 0, 0, 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78, 0, 0, 0, 0, 0}; - const byte *d; + constexpr const byte *d = dd + 7; const N_BELE_RTP::AbstractPolicy *bele; - d = dd + 7; assert_noexcept(upx_adler32(d, 4) == 0x09f003f7); assert_noexcept(upx_adler32(d, 4, 0) == 0x09ec03f6); assert_noexcept(upx_adler32(d, 4, 1) == 0x09f003f7); @@ -709,6 +748,8 @@ void upx_compiler_sanity_check(void) noexcept { assert_noexcept(get_be32(d) == 0xfffefdfc); assert_noexcept(bele->get32(d) == 0xfffefdfc); assert_noexcept(get_be32_signed(d) == -66052); + assert_noexcept(get_be64(d) == 0xfffefdfcfbfaf9f8ULL); + assert_noexcept(get_be64_signed(d) == -283686952306184LL); bele = &N_BELE_RTP::le_policy; assert_noexcept(get_le16(d) == 0xfeff); assert_noexcept(bele->get16(d) == 0xfeff); @@ -719,16 +760,41 @@ void upx_compiler_sanity_check(void) noexcept { assert_noexcept(get_le32(d) == 0xfcfdfeff); assert_noexcept(bele->get32(d) == 0xfcfdfeff); assert_noexcept(get_le32_signed(d) == -50462977); + assert_noexcept(get_le64(d) == 0xf8f9fafbfcfdfeffULL); assert_noexcept(get_le64_signed(d) == -506097522914230529LL); +#if defined(upx_is_constant_evaluated) + static_assert(get_be16(d) == 0xfffe); + static_assert(get_be16_signed(d) == -2); + static_assert(get_be24(d) == 0xfffefd); + static_assert(get_be24_signed(d) == -259); + static_assert(get_be32(d) == 0xfffefdfc); + static_assert(get_be32_signed(d) == -66052); + static_assert(get_be64(d) == 0xfffefdfcfbfaf9f8ULL); + static_assert(get_be64_signed(d) == -283686952306184LL); + static_assert(get_le16(d) == 0xfeff); + static_assert(get_le16_signed(d) == -257); + static_assert(get_le24(d) == 0xfdfeff); + static_assert(get_le24_signed(d) == -131329); + static_assert(get_le32(d) == 0xfcfdfeff); + static_assert(get_le32_signed(d) == -50462977); + static_assert(get_le64(d) == 0xf8f9fafbfcfdfeffULL); + static_assert(get_le64_signed(d) == -506097522914230529LL); +#endif assert_noexcept(find_be16(d, 2, 0xfffe) == 0); assert_noexcept(find_le16(d, 2, 0xfeff) == 0); assert_noexcept(find_be32(d, 4, 0xfffefdfc) == 0); assert_noexcept(find_le32(d, 4, 0xfcfdfeff) == 0); - d += 12; - assert_noexcept(get_be16_signed(d) == 32638); - assert_noexcept(get_be24_signed(d) == 8355453); - assert_noexcept(get_be32_signed(d) == 2138996092); - assert_noexcept(get_be64_signed(d) == 9186918263483431288LL); + constexpr const byte *e = d + 12; + assert_noexcept(get_be16_signed(e) == 32638); + assert_noexcept(get_be24_signed(e) == 8355453); + assert_noexcept(get_be32_signed(e) == 2138996092); + assert_noexcept(get_be64_signed(e) == 9186918263483431288LL); +#if defined(upx_is_constant_evaluated) + static_assert(get_be16_signed(e) == 32638); + static_assert(get_be24_signed(e) == 8355453); + static_assert(get_be32_signed(e) == 2138996092); + static_assert(get_be64_signed(e) == 9186918263483431288LL); +#endif } #if DEBUG >= 1 { diff --git a/src/conf.h b/src/conf.h index a0d8c116..094b5497 100644 --- a/src/conf.h +++ b/src/conf.h @@ -121,6 +121,12 @@ inline constexpr bool upx_is_integral_v = upx_is_integral::value; #define upx_alignas_max alignas(std::max_align_t) #endif +#if __cplusplus >= 202002L +#define upx_is_constant_evaluated std::is_constant_evaluated +#elif __has_builtin(__builtin_is_constant_evaluated) +#define upx_is_constant_evaluated __builtin_is_constant_evaluated +#endif + /************************************************************************* // core **************************************************************************/ @@ -289,6 +295,14 @@ typedef upx_int64_t upx_off_t; #endif #endif +// some platforms may provide their own system bswapXX() functions, so rename to avoid conflicts +#undef bswap16 +#undef bswap32 +#undef bswap64 +#define bswap16 upx_bswap16 +#define bswap32 upx_bswap32 +#define bswap64 upx_bswap64 + // avoid warnings about shadowing global symbols #undef _base #undef basename @@ -347,7 +361,14 @@ inline void NO_fprintf(FILE *, const char *, ...) noexcept attribute_format(2, 3 inline void NO_printf(const char *, ...) noexcept {} inline void NO_fprintf(FILE *, const char *, ...) noexcept {} -#if __has_builtin(__builtin_memcpy_inline) +#if __has_builtin(__builtin_memcmp) +#define upx_memcmp_inline __builtin_memcmp +#elif defined(__clang__) || defined(__GNUC__) +#define upx_memcmp_inline __builtin_memcmp +#else +#define upx_memcmp_inline memcmp +#endif +#if __has_builtin(__builtin_memcpy_inline) && 0 // TODO later: clang constexpr limitation? #define upx_memcpy_inline __builtin_memcpy_inline #elif __has_builtin(__builtin_memcpy) #define upx_memcpy_inline __builtin_memcpy diff --git a/src/help.cpp b/src/help.cpp index 98e7998c..9302782c 100644 --- a/src/help.cpp +++ b/src/help.cpp @@ -591,6 +591,9 @@ void show_sysinfo(const char *options_var) { cf_print("__GLIBC_MINOR__", "%lld", __GLIBC_MINOR__ + 0); #endif // misc compilation options +#if defined(upx_is_constant_evaluated) + cf_print("upx_is_constant_evaluated", "%lld", 1, 3); +#endif #if defined(UPX_CONFIG_DISABLE_WSTRICT) cf_print("UPX_CONFIG_DISABLE_WSTRICT", "%lld", UPX_CONFIG_DISABLE_WSTRICT + 0, 3); #endif diff --git a/src/util/cxxlib.h b/src/util/cxxlib.h index f70058ce..b735aad1 100644 --- a/src/util/cxxlib.h +++ b/src/util/cxxlib.h @@ -177,23 +177,23 @@ forceinline constexpr bool has_single_bit(T x) noexcept { **************************************************************************/ template -inline T align_down(const T &x, const T &alignment) noexcept { - assert_noexcept(has_single_bit(alignment)); - T r; +inline constexpr T align_down(const T &x, const T &alignment) noexcept { + // assert_noexcept(has_single_bit(alignment)); + T r = {}; r = (x / alignment) * alignment; return r; } template -inline T align_up(const T &x, const T &alignment) noexcept { - assert_noexcept(has_single_bit(alignment)); - T r; +inline constexpr T align_up(const T &x, const T &alignment) noexcept { + // assert_noexcept(has_single_bit(alignment)); + T r = {}; r = ((x + (alignment - 1)) / alignment) * alignment; return r; } template -inline T align_gap(const T &x, const T &alignment) noexcept { - assert_noexcept(has_single_bit(alignment)); - T r; +inline constexpr T align_gap(const T &x, const T &alignment) noexcept { + // assert_noexcept(has_single_bit(alignment)); + T r = {}; r = align_up(x, alignment) - x; return r; } @@ -232,14 +232,14 @@ struct UnsignedSizeOf { // a static_cast that does not trigger -Wcast-align warnings template -forceinline Result ptr_static_cast(From *ptr) noexcept { +forceinline constexpr Result ptr_static_cast(From *ptr) noexcept { static_assert(std::is_pointer_v); static_assert(!std::is_const_v >); // enforce same constness // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) return static_cast(static_cast(ptr)); } template -forceinline Result ptr_static_cast(const From *ptr) noexcept { +forceinline constexpr Result ptr_static_cast(const From *ptr) noexcept { static_assert(std::is_pointer_v); static_assert(std::is_const_v >); // required // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) diff --git a/src/util/system_headers.h b/src/util/system_headers.h index 887da7c0..e1a25c93 100644 --- a/src/util/system_headers.h +++ b/src/util/system_headers.h @@ -119,6 +119,9 @@ static_assert(sizeof(void *) == sizeof(long)); #endif // C++ freestanding headers +#if __cplusplus >= 202002L +#include +#endif #include #include #include