diff --git a/librz/include/rz_endian.h b/librz/include/rz_endian.h index 9d40d93ac0..044e5d749a 100644 --- a/librz/include/rz_endian.h +++ b/librz/include/rz_endian.h @@ -1813,6 +1813,60 @@ static inline ut64 rz_swap_ut64(ut64 val) { } #endif +/** + * \def rz_swap_2b_ut64 + * \brief Swaps pairs of 2 bytes in a 64bit value + * + * # Example + * + * \code{.c} + * ut32 x = 0x8899AABBCCDDEEFF; + * ut32 result = 0x9988BBAADDCCFFEE; + * assert(rz_swap_2b_ut64(x) == result); + * \endcode + */ +static inline ut64 rz_swap_2b_ut64(ut64 val) { + val = ((val & 0xff00ff00ff00ff00) >> 8) | ((val & 0x00ff00ff00ff00ff) << 8); + return val; +} + +/** + * \def rz_swap_4b_ut64 + * \brief Swaps pairs of 4 bytes in a 64bit value + * + * # Example + * + * \code{.c} + * ut32 x = 0x8899AABBCCDDEEFF; + * ut32 result = 0xBBAA9988FFEEDDCC; + * assert(rz_swap_4b_ut64(x) == result); + * \endcode + */ +static inline ut64 rz_swap_4b_ut64(ut64 val) { + val = ((val & 0xff000000ff000000) >> 24) | + ((val & 0x00ff000000ff0000) >> 8) | + ((val & 0x0000ff000000ff00) << 8) | + ((val & 0x000000ff000000ff) << 24); + return val; +} + +/** + * \def rz_swap_2b_ut32 + * \brief Swaps pairs of 2 bytes in a 32bit value + * + * # Example + * + * \code{.c} + * ut32 x = 0xCCDDEEFF; + * ut32 result = 0xDDCCFFEE; + * assert(rz_swap_2b_ut32(x) == result); + * \endcode + */ +static inline ut32 rz_swap_2b_ut32(ut32 val) { + val = ((val & 0xff00ff00) >> 8) | ((val & 0x00ff00ff) << 8); + return val; +} + /* Some "secured" functions, to do basic operation (mul, sub, add...) on integers */ /** diff --git a/librz/include/rz_types_base.h b/librz/include/rz_types_base.h index d1edc89fa0..5afb67fe2e 100644 --- a/librz/include/rz_types_base.h +++ b/librz/include/rz_types_base.h @@ -27,6 +27,7 @@ #define st16 short #define ut8 unsigned char #define st8 signed char +#define utptr uintptr_t #define boolt int #if defined(_MSC_VER) diff --git a/librz/include/rz_userconf.h.in b/librz/include/rz_userconf.h.in index 47e3ff4bba..b508a2439c 100644 --- a/librz/include/rz_userconf.h.in +++ b/librz/include/rz_userconf.h.in @@ -55,6 +55,7 @@ #define HAVE___BUILTIN_BSWAP32 @HAVE___BUILTIN_BSWAP32@ #define HAVE___BUILTIN_BSWAP64 @HAVE___BUILTIN_BSWAP64@ #define HAVE___BUILTIN_CLZLL @HAVE___BUILTIN_CLZLL@ +#define HAVE___BUILTIN_CTZLL @HAVE___BUILTIN_CTZLL@ #define HAVE_POSIX_MEMALIGN @HAVE_POSIX_MEMALIGN@ #define HAVE__ALIGNED_MALLOC @HAVE__ALIGNED_MALLOC@ diff --git a/librz/include/rz_util/rz_bits.h b/librz/include/rz_util/rz_bits.h index 4e263acb4e..13f4e945f3 100644 --- a/librz/include/rz_util/rz_bits.h +++ b/librz/include/rz_util/rz_bits.h @@ -33,13 +33,63 @@ DEFINE_COUNT_ONES(ut32); DEFINE_COUNT_ONES(ut16); DEFINE_COUNT_ONES(ut8); +/** + * \brief Count trailing zeros of \p v. + * If v == 0 it returns 64. + * + * \param v The value to count the trailing zeros for. + * + * \return The number of trailing zeros. + */ +static inline size_t rz_bits_trailing_zeros(ut64 v) { + if (v == 0) { + return 64; + } +#if HAVE___BUILTIN_CTZLL + return __builtin_ctzll(v); +#else + // src: https://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightBinSearch + size_t c; + if (v & 0x1) { + // special case for odd v (assumed to happen half of the time) + return 0; + } + c = 1; + if ((v & 0xffffffff) == 0) { + v >>= 32; + c += 32; + } + if ((v & 0xffff) == 0) { + v >>= 16; + c += 16; + } + if ((v & 0xff) == 0) { + v >>= 8; + c += 8; + } + if ((v & 0xf) == 0) { + v >>= 4; + c += 4; + } + if ((v & 0x3) == 0) { + v >>= 2; + c += 2; + } + c -= v & 0x1; + return c; +#endif +} + /** * \brief Get the number of leading zeros of a 64-bit integer in binary representation. * \param x the 64-bit integer * \return the number of leading zeros */ static inline int rz_bits_leading_zeros(ut64 x) { -#if HAS___BUILTIN_CLZLL + if (x == 0) { + return 64; + } +#if HAVE___BUILTIN_CLZLL return __builtin_clzll(x); #else int n = 0; diff --git a/librz/include/rz_util/rz_mem.h b/librz/include/rz_util/rz_mem.h index d4d86af939..19c9a2da28 100644 --- a/librz/include/rz_util/rz_mem.h +++ b/librz/include/rz_util/rz_mem.h @@ -1,6 +1,7 @@ #ifndef RZ_MEM_H #define RZ_MEM_H +#include #include #ifdef __cplusplus @@ -37,6 +38,25 @@ RZ_API int rz_mem_count(const ut8 **addr); RZ_API bool rz_mem_is_printable(const ut8 *a, int la); RZ_API bool rz_mem_is_zero(const ut8 *b, int l); RZ_API ut64 rz_mem_align_padding(const ut64 address, ut64 alignment); +RZ_API RZ_OWN ut8 *rz_mem_copy_offset(const ut8 *buf, size_t buf_size, size_t offset); +RZ_API RZ_OWN ut8 *rz_mem_swap_bytes_2(RZ_NONNULL const ut8 *buf, size_t buf_size); +RZ_API RZ_OWN ut8 *rz_mem_swap_bytes_2_inplace(RZ_OUT RZ_NONNULL ut8 *buf, size_t buf_size); +RZ_API RZ_OWN ut8 *rz_mem_swap_bytes_4(RZ_NONNULL const ut8 *buf, size_t buf_size); +RZ_API RZ_OWN ut8 *rz_mem_swap_bytes_4_inplace(RZ_OUT RZ_NONNULL ut8 *buf, size_t buf_size); + +/** + * \brief Returns the alignment of the \p ptr. + * + * \param ptr The pointer to get the alignment for. + * + * \return Returns the pointer alignment or UT64_MAX if \p ptr == NULL or ((utptr) ptr) == 0. + */ +static inline ut64 rz_mem_ptr_alignment(RZ_NONNULL const void *ptr) { + if (ptr == NULL || ((utptr)ptr) == 0) { + return UT64_MAX; + } + return 1ull << rz_bits_trailing_zeros((utptr)ptr); +} #ifdef __cplusplus } diff --git a/librz/util/mem.c b/librz/util/mem.c index efe8297ed0..58d2f49108 100644 --- a/librz/util/mem.c +++ b/librz/util/mem.c @@ -331,3 +331,245 @@ RZ_API void rz_mem_memzero(void *dst, size_t l) { #endif #endif } + +/** + * \brief Makes a copy of the buffer \p buf but starts copying data at \p offset. + * That is: `rz_mem_align_byte(buf, n, i)` will return a copy of `buf[i:n]`. + * The bytes `[n - i:n]` of the returned buffer are set to 0x00. + * + * \param buf The buffer to copy. + * \param buf_size The size of \p buf in bytes. If 0, this function just performce a memcpy. + * \param offset The offset to start copying from. If larger than \p buf_size, + * it returns a zeroed buffer of size \p buf_size. + * + * \return The copied buffer or NULL in case of failure. If \p offset >= \p buf_size + * the returned buffer is all zeros. + * + * NOTE: This function is useful to align the data in \p buf to a certain offset. + * E.g. reading ut64 values from \p buf + 3 would be undefined behavior and fail under certain conditions. + * Instead this function can be used to get a copy of the buffer at this offset: + * + * Example: + * ```c + * // This is undefined behavior, because the memory access is misaligned for ut64 values. + * ut64 x = *((ut64 *)buf + 3); + * + * // Instead you can align buf with this function: + * ut8 *out = rz_mem_align_byte(buf, buf_size, 3); + * ut64 v = *((ut64 *)out); + * ``` + */ +RZ_API RZ_OWN ut8 *rz_mem_copy_offset(const ut8 *buf, size_t buf_size, size_t offset) { + rz_return_val_if_fail(buf && buf_size > 0, NULL); + ut8 *dst = RZ_NEWS0(ut8, buf_size); + if (offset >= buf_size) { + return dst; + } + if (!rz_mem_copy(dst, buf_size + offset, buf + offset, buf_size - offset)) { + free(dst); + return NULL; + } + return dst; +} + +/** + * \brief Swaps the bytes in 2 byte blocks from the given buffer and returns + * the result. + * Remainders of less than 2 bytes at the end of the buffer won't be swapped. + * + * \param buf The input buffer. + * \param buf_size The size of the input buffer. Must be greater than 0. + * + * \return A clone of the input buffer with swapped bytes or NULL in case of failure. + * + * NOTE: This function can be used to change the endianness of 2 byte values + * in the given buffer. + * + * Examples: + * ```c + * const ut8 a[4] = { 0xff, 0x00, 0x99, 0x00 }; + * const ut8 b[4] = { 0x00, 0xff, 0x00, 0x99 }; + * ut8 *swapped = rz_mem_swap_bytes_2(a); + * assert(memcmp(swapped, b, sizeof(a)) == 0); + * ``` + * + * ```c + * const ut8 a[4] = { 0xff, 0x00, 0x99, 0x00, 0x11 }; + * const ut8 b[4] = { 0x00, 0xff, 0x00, 0x99, 0x11 }; + * ut8 *swapped = rz_mem_swap_bytes_2(a); + * assert(memcmp(swapped, b, sizeof(a)) == 0); + * ``` + */ +RZ_API RZ_OWN ut8 *rz_mem_swap_bytes_2(RZ_NONNULL const ut8 *buf, size_t buf_size) { + rz_return_val_if_fail(buf && buf_size != 0, NULL); + ut8 *dst = RZ_NEWS0(ut8, buf_size); + if (!dst) { + return NULL; + } + if (!rz_mem_copy(dst, buf_size, buf, buf_size)) { + free(dst); + return NULL; + } + return rz_mem_swap_bytes_2_inplace(dst, buf_size); +} + +/** + * \brief Swaps the bytes in 2 byte blocks from the given buffer and returns + * the result. + * Remainders of less than 2 bytes at the end of the buffer won't be swapped. + * + * \param buf The input buffer. + * \param buf_size The size of the input buffer. Must be greater than 0. + * + * \return A clone of the input buffer with swapped bytes or NULL in case of failure. + * + * NOTE: This function can be used to change the endianness of 2 byte values + * in the given buffer. + * + * Examples: + * ```c + * ut8 a[4] = { 0xff, 0x00, 0x99, 0x00 }; + * const ut8 b[4] = { 0x00, 0xff, 0x00, 0x99 }; + * ut8 *swapped = rz_mem_swap_bytes_2(a); + * assert(a == swapped); + * assert(memcmp(swapped, b, sizeof(a)) == 0); + * ``` + * + * ```c + * ut8 a[4] = { 0xff, 0x00, 0x99, 0x00, 0x11 }; + * const ut8 b[4] = { 0x00, 0xff, 0x00, 0x99, 0x11 }; + * ut8 *swapped = rz_mem_swap_bytes_2(a); + * assert(a == swapped); + * assert(memcmp(swapped, b, sizeof(a)) == 0); + * ``` + */ +RZ_API RZ_OWN ut8 *rz_mem_swap_bytes_2_inplace(RZ_OUT RZ_NONNULL ut8 *dst, size_t buf_size) { + rz_return_val_if_fail(dst && buf_size, NULL); + size_t al = rz_mem_ptr_alignment(dst); + if (al < 2) { + // malloc guarantees to return an aligned pointer for all data which fits + // into the allocated memory. + // So, if the pointer is only aligned to less than 2 bytes, + // it means buf_size was == 1. + // Hence we return simply a clone of the buffer. + return dst; + } + + ut64 *dst_64 = (ut64 *)dst; + while (buf_size >= 8) { + *dst_64 = rz_swap_2b_ut64(*dst_64); + dst_64++; + buf_size -= 8; + } + ut32 *dst_32 = (ut32 *)dst_64; + while (buf_size >= 4) { + *dst_32 = rz_swap_2b_ut32(*dst_32); + dst_32++; + buf_size -= 4; + } + ut16 *dst_16 = (ut16 *)dst_32; + while (buf_size >= 2) { + *dst_16 = rz_swap_ut16(*dst_16); + dst_16++; + buf_size -= 2; + } + return dst; +} + +/** + * \brief Swaps the bytes in 4 byte blocks from the given buffer and returns + * the result. + * Remainders of less than 4 bytes at the end of the buffer won't be swapped. + * + * \param buf The input buffer. + * \param buf_size The size of the input buffer. Must be greater than 0. + * + * \return A clone of the input buffer with swapped bytes or NULL in case of failure. + * + * NOTE: This function can be used to change the endianness of 4 byte values + * in the given buffer. + * + * Examples: + * ```c + * const ut8 a[4] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; + * const ut8 b[4] = { 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04 }; + * ut8 *swapped = rz_mem_swap_bytes_4(a); + * assert(memcmp(swapped, b, sizeof(a)) == 0); + * ``` + * + * ```c + * const ut8 a[4] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xff, 0xfe }; + * const ut8 b[4] = { 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0xff, 0xfe }; + * ut8 *swapped = rz_mem_swap_bytes_4(a); + * assert(memcmp(swapped, b, sizeof(a)) == 0); + * ``` + */ +RZ_API RZ_OWN ut8 *rz_mem_swap_bytes_4(RZ_NONNULL const ut8 *buf, size_t buf_size) { + rz_return_val_if_fail(buf && buf_size != 0, NULL); + ut8 *dst = RZ_NEWS0(ut8, buf_size); + if (!dst) { + return NULL; + } + if (!rz_mem_copy(dst, buf_size, buf, buf_size)) { + free(dst); + return NULL; + } + return rz_mem_swap_bytes_4_inplace(dst, buf_size); +} + +/** + * \brief Swaps the bytes in 4 byte blocks from the given buffer and returns + * the result. + * Remainders of less than 4 bytes at the end of the buffer won't be swapped. + * + * \param buf The input buffer. + * \param buf_size The size of the input buffer. Must be greater than 0. + * + * \return The input buffer with swapped bytes or NULL in case of failure. + * + * NOTE: This function can be used to change the endianness of 4 byte values + * in the given buffer. + * + * Examples: + * ```c + * ut8 a[4] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; + * const ut8 b[4] = { 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04 }; + * ut8 *swapped = rz_mem_swap_bytes_4(a); + * assert(a == swapped); + * assert(memcmp(swapped, b, sizeof(a)) == 0); + * ``` + * + * ```c + * ut8 a[4] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xff, 0xfe }; + * const ut8 b[4] = { 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0xff, 0xfe }; + * ut8 *swapped = rz_mem_swap_bytes_4(a); + * assert(a == swapped); + * assert(memcmp(swapped, b, sizeof(a)) == 0); + * ``` + */ +RZ_API RZ_OWN ut8 *rz_mem_swap_bytes_4_inplace(RZ_OUT RZ_NONNULL ut8 *dst, size_t buf_size) { + rz_return_val_if_fail(dst && buf_size, NULL); + size_t al = rz_mem_ptr_alignment(dst); + if (al < 4) { + // malloc guarantees to return an aligned pointer for all data which fits + // into the allocated memory. + // So, if the pointer is only aligned to less than 4 bytes, + // it means buf_size was <= 3. + // Hence we return simply a clone of the buffer. + return dst; + } + + ut64 *dst_64 = (ut64 *)dst; + while (buf_size >= 8) { + *dst_64 = rz_swap_4b_ut64(*dst_64); + dst_64++; + buf_size -= 8; + } + ut32 *dst_32 = (ut32 *)dst_64; + while (buf_size >= 4) { + *dst_32 = rz_swap_ut32(*dst_32); + dst_32++; + buf_size -= 4; + } + return dst; +} diff --git a/meson.build b/meson.build index 3ed622bb8c..bffb8ed611 100644 --- a/meson.build +++ b/meson.build @@ -500,6 +500,7 @@ foreach it : ccs ['__builtin_bswap32', '', []], ['__builtin_bswap64', '', []], ['__builtin_clzll', '', []], + ['__builtin_ctzll', '', []], ['posix_memalign', '#include ', []], ['_aligned_malloc', '#include ', []], ] diff --git a/test/unit/test_bits.c b/test/unit/test_bits.c index 46ce01cc52..eb6fa965eb 100644 --- a/test/unit/test_bits.c +++ b/test/unit/test_bits.c @@ -3,6 +3,7 @@ #include #include "minunit.h" +#include "rz_util/rz_bits.h" bool test_rz_bits_count(void) { mu_assert_eq(rz_bits_count_ones_ut64(0xffffffffffffffff), 64, "Bit count mismatch."); @@ -35,6 +36,15 @@ bool test_rz_bits_count(void) { mu_end; } +bool test_rz_bits_trailing_zero(void) { + mu_assert_eq(rz_bits_trailing_zeros(0), 64, "Bit count mismatch."); + for (size_t i = 1, j = 0; i != 0; i <<= 1, j++) { + mu_assert_eq(rz_bits_trailing_zeros(i), j, "Bit count mismatch."); + } + + mu_end; +} + bool test_rz_bits_spread(void) { mu_assert_eq(rz_bits_spread(0xffffffffffffffff, 0xffffffffffffffff), 0xffffffffffffffff, "Spread mismatch."); mu_assert_eq(rz_bits_spread(0, 0xffffffffffffffff), 0, "Spread mismatch."); @@ -52,6 +62,7 @@ bool test_rz_bits_spread(void) { bool all_tests() { mu_run_test(test_rz_bits_count); mu_run_test(test_rz_bits_spread); + mu_run_test(test_rz_bits_trailing_zero); return tests_passed != tests_run; } diff --git a/test/unit/test_endian.c b/test/unit/test_endian.c index 6dfd1f16c0..13aa1298e5 100644 --- a/test/unit/test_endian.c +++ b/test/unit/test_endian.c @@ -364,6 +364,20 @@ bool test_rz_swap_ut64(void) { mu_end; } +bool test_rz_swap_4b_ut64(void) { + ut64 a = 0x1122334455667788; + ut64 b = rz_swap_4b_ut64(a); + mu_assert_eq(b, 0x4433221188776655, "rz_swap_4b_ut64"); + mu_end; +} + +bool test_rz_swap_2b_ut64(void) { + ut64 a = 0x1122334455667788; + ut64 b = rz_swap_2b_ut64(a); + mu_assert_eq(b, 0x2211443366558877, "rz_swap_2b_ut64"); + mu_end; +} + bool test_rz_swap_ut32(void) { ut32 a = 0x11223344; ut32 b = rz_swap_ut32(a); @@ -371,6 +385,13 @@ bool test_rz_swap_ut32(void) { mu_end; } +bool test_rz_swap_2b_ut32(void) { + ut32 a = 0x11223344; + ut32 b = rz_swap_2b_ut32(a); + mu_assert_eq(b, 0x22114433, "rz_swap_2b_ut32"); + mu_end; +} + bool test_rz_swap_ut16(void) { ut16 a = 0x1122; ut16 b = rz_swap_ut16(a); @@ -494,7 +515,10 @@ int all_tests() { mu_run_test(test_endian); mu_run_test(test_rz_swap_ut64); + mu_run_test(test_rz_swap_4b_ut64); + mu_run_test(test_rz_swap_2b_ut64); mu_run_test(test_rz_swap_ut32); + mu_run_test(test_rz_swap_2b_ut32); mu_run_test(test_rz_swap_ut16); mu_run_test(test_be); mu_run_test(test_le); diff --git a/test/unit/test_mem.c b/test/unit/test_mem.c index 1c0d216a78..656d40ee2d 100644 --- a/test/unit/test_mem.c +++ b/test/unit/test_mem.c @@ -27,8 +27,211 @@ bool test_rz_mem_align_padding(void) { mu_end; } +bool test_rz_mem_align_byte(void) { + const ut8 one_byte[1] = { 0xff }; + const ut8 one_byte_a_2[1] = { 0x00 }; + const ut8 two_bytes[2] = { 0x00, 0x01 }; + const ut8 two_bytes_a_2[2] = { 0x01, 0x00 }; + const ut8 two_bytes_a_3[2] = { 0x00, 0x00 }; + const ut8 two_bytes_a_8[2] = { 0x00, 0x00 }; + const ut8 seven_bytes[7] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const ut8 seven_bytes_a_2[7] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00 }; + const ut8 seven_bytes_a_3[7] = { 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00 }; + const ut8 seven_bytes_a_7[7] = { 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + const ut8 seven_bytes_a_8[7] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + const ut8 eight_bytes[8] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; + const ut8 eight_bytes_a_2[8] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00 }; + const ut8 eight_bytes_a_8[8] = { 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + const ut8 nine_bytes[9] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; + const ut8 nine_bytes_a_8[9] = { 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + // clang-format off + const ut8 block[0x20] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + }; + const ut8 block_a_2[0x20] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00 + }; + const ut8 block_a_8[0x20] = { + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, + 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + // clang-format on + + ut8 *test_out; + +#define TEST(input, expected, alignment, msg) \ + test_out = rz_mem_copy_offset(input, sizeof(input), alignment); \ + mu_assert_notnull(test_out, "NULL check failed"); \ + mu_assert_memeq(test_out, expected, sizeof(expected), msg); \ + mu_assert_eq(((ut64)test_out) % 8, 0, "Address is not aligned to 8."); \ + free(test_out); + + TEST(one_byte, one_byte, 0, "Alignment of 1 should be memcpy") + TEST(block, block, 0, "Alignment of 1 should be memcpy") + + TEST(two_bytes, two_bytes_a_3, 3, "Alignment larger than buffer") + TEST(two_bytes, two_bytes_a_8, 8, "Alignment larger than buffer") + + TEST(one_byte, one_byte_a_2, 2, "Invalid alignment result") + + TEST(two_bytes, two_bytes_a_2, 1, "Invalid alignment result") + + TEST(seven_bytes, seven_bytes_a_2, 1, "Invalid alignment result") + TEST(seven_bytes, seven_bytes_a_3, 2, "Invalid alignment result") + TEST(seven_bytes, seven_bytes_a_7, 6, "Invalid alignment result") + TEST(seven_bytes, seven_bytes_a_8, 7, "Invalid alignment result") + + TEST(eight_bytes, eight_bytes_a_2, 1, "Invalid alignment result") + TEST(eight_bytes, eight_bytes_a_8, 7, "Invalid alignment result") + + TEST(nine_bytes, nine_bytes_a_8, 7, "Invalid alignment result") + + TEST(block, block_a_2, 1, "Invalid alignment result") + TEST(block, block_a_8, 7, "Invalid alignment result") + +#undef TEST + + mu_end; +} + +bool test_rz_mem_ptr_alignment(void) { + mu_assert_eq(rz_mem_ptr_alignment(NULL), UT64_MAX, "Error case failed."); + mu_assert_eq(rz_mem_ptr_alignment((void *)0), UT64_MAX, "Error case failed."); + for (ut64 i = 1; i != 0; i <<= 1) { + mu_assert_eq(rz_mem_ptr_alignment((void *)i), i, "Pointer alignment mismatches."); + } + + mu_end; +} + +bool test_rz_mem_byte_swap_2(void) { + const ut8 buf_1[] = { 0xff }; + const ut8 buf_1s[] = { 0xff }; + const ut8 buf_2[] = { 0xff, 0x00 }; + const ut8 buf_2s[] = { 0x00, 0xff }; + const ut8 buf_3[] = { 0xff, 0x00, 0xfe }; + const ut8 buf_3s[] = { 0x00, 0xff, 0xfe }; + const ut8 buf_4[] = { 0xff, 0x00, 0xfe, 0x00 }; + const ut8 buf_4s[] = { 0x00, 0xff, 0x00, 0xfe }; + const ut8 buf_5[] = { 0xff, 0x00, 0xfe, 0x00, 0xfd }; + const ut8 buf_5s[] = { 0x00, 0xff, 0x00, 0xfe, 0xfd }; + const ut8 buf_8[] = { 0xff, 0x00, 0xfe, 0x00, 0xfd, 0x00, 0xfc, 0x00 }; + const ut8 buf_8s[] = { 0x00, 0xff, 0x00, 0xfe, 0x00, 0xfd, 0x00, 0xfc }; + const ut8 buf_9[] = { 0xff, 0x00, 0xfe, 0x00, 0xfd, 0x00, 0xfc, 0x00, 0xfb }; + const ut8 buf_9s[] = { 0x00, 0xff, 0x00, 0xfe, 0x00, 0xfd, 0x00, 0xfc, 0xfb }; + + // clang-format off + const ut8 block_32[] = { + 0xff, 0x00, 0xfe, 0x00, 0xfd, 0x00, 0xfc, 0x00, + 0xfb, 0x00, 0xfa, 0x00, 0xf9, 0x00, 0xf8, 0x00, + 0xf7, 0x00, 0xf6, 0x00, 0xf5, 0x00, 0xf4, 0x00, + 0xf3, 0x00, 0xf2, 0x00, 0xf1, 0x00, 0xf0, 0x00 + }; + const ut8 block_32s[] = { + 0x00, 0xff, 0x00, 0xfe, 0x00, 0xfd, 0x00, 0xfc, + 0x00, 0xfb, 0x00, 0xfa, 0x00, 0xf9, 0x00, 0xf8, + 0x00, 0xf7, 0x00, 0xf6, 0x00, 0xf5, 0x00, 0xf4, + 0x00, 0xf3, 0x00, 0xf2, 0x00, 0xf1, 0x00, 0xf0 + }; + // clang-format on + + ut8 *test_out; + +#define TEST(input, expected, msg) \ + test_out = rz_mem_swap_bytes_2(input, sizeof(input)); \ + mu_assert_notnull(test_out, "NULL check failed"); \ + mu_assert_memeq(test_out, expected, sizeof(expected), msg); \ + free(test_out); + + TEST(buf_1, buf_1s, "Swap failed."); + TEST(buf_2, buf_2s, "Swap failed."); + TEST(buf_3, buf_3s, "Swap failed."); + TEST(buf_4, buf_4s, "Swap failed."); + TEST(buf_5, buf_5s, "Swap failed."); + TEST(buf_8, buf_8s, "Swap failed."); + TEST(buf_9, buf_9s, "Swap failed."); + TEST(block_32, block_32s, "Swap failed."); + +#undef TEST + + mu_end; +} + +bool test_rz_mem_byte_swap_4(void) { + const ut8 buf_1[] = { 0xff }; + const ut8 buf_1s[] = { 0xff }; + const ut8 buf_3[] = { 0xff, 0x00, 0xfe }; + const ut8 buf_3s[] = { 0xff, 0x00, 0xfe }; + const ut8 buf_4[] = { 0x00, 0x01, 0x02, 0x03 }; + const ut8 buf_4s[] = { 0x03, 0x02, 0x01, 0x00 }; + const ut8 buf_5[] = { 0x00, 0x01, 0x02, 0x03, 0x04 }; + const ut8 buf_5s[] = { 0x03, 0x02, 0x01, 0x00, 0x04 }; + const ut8 buf_8[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; + const ut8 buf_8s[] = { 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04 }; + const ut8 buf_9[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xff }; + const ut8 buf_9s[] = { 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0xff }; + + // clang-format off + const ut8 block_34[] = { + 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, + 0x55, 0x44 + }; + const ut8 block_34s[] = { + 0x03, 0x02, 0x01, 0x00, + 0x07, 0x06, 0x05, 0x04, + 0x0b, 0x0a, 0x09, 0x08, + 0x0f, 0x0e, 0x0d, 0x0c, + 0x13, 0x12, 0x11, 0x10, + 0x17, 0x16, 0x15, 0x14, + 0x1b, 0x1a, 0x19, 0x18, + 0x1f, 0x1e, 0x1d, 0x1c, + 0x55, 0x44 + }; + // clang-format on + + ut8 *test_out; + +#define TEST(input, expected, msg) \ + test_out = rz_mem_swap_bytes_4(input, sizeof(input)); \ + mu_assert_notnull(test_out, "NULL check failed"); \ + mu_assert_memeq(test_out, expected, sizeof(expected), msg); \ + free(test_out); + + TEST(buf_1, buf_1s, "Swap failed."); + TEST(buf_3, buf_3s, "Swap failed."); + TEST(buf_4, buf_4s, "Swap failed."); + TEST(buf_5, buf_5s, "Swap failed."); + TEST(buf_8, buf_8s, "Swap failed."); + TEST(buf_9, buf_9s, "Swap failed."); + TEST(block_34, block_34s, "Swap failed."); + +#undef TEST + + mu_end; +} + bool all_tests() { mu_run_test(test_rz_mem_align_padding); + mu_run_test(test_rz_mem_align_byte); + mu_run_test(test_rz_mem_ptr_alignment); + mu_run_test(test_rz_mem_byte_swap_2); + mu_run_test(test_rz_mem_byte_swap_4); return tests_passed != tests_run; }