Add several memory helpers (required for speed up of string search). (#5372)

* Add function to memcpy memory with offset.

* Add count trailing zeros function.

* Add function to determine pointer alignment.

* Add helper to swap sequences of 2 bytes.

* Add helper to swap sequences of 4 bytes.

* Print pointer in warning

* Fix tests

* Use uintptr_t type to prevent undefined behavior.

* Remove alignment requirement and rely on malloc promises.

* Add in place byte swap functions.

* Move the endianness swapping into util/rz_endian

* Use GNU builtin __builtin_ctzll/clzll.

Also fixes the include guards of __builtin_clzll
and handles the undefined case of x == 0 of it.

* Add doxygen
This commit is contained in:
Rot127 2025-09-16 16:07:44 +00:00 committed by GitHub
parent 976671d660
commit fdc9ecbacc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 608 additions and 1 deletions

View file

@ -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 */
/**

View file

@ -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)

View file

@ -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@

View file

@ -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;

View file

@ -1,6 +1,7 @@
#ifndef RZ_MEM_H
#define RZ_MEM_H
#include <rz_util/rz_bits.h>
#include <rz_types.h>
#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
}

View file

@ -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;
}

View file

@ -500,6 +500,7 @@ foreach it : ccs
['__builtin_bswap32', '', []],
['__builtin_bswap64', '', []],
['__builtin_clzll', '', []],
['__builtin_ctzll', '', []],
['posix_memalign', '#include <stdlib.h>', []],
['_aligned_malloc', '#include <malloc.h>', []],
]

View file

@ -3,6 +3,7 @@
#include <rz_util.h>
#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;
}

View file

@ -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);

View file

@ -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;
}