From ed56c56d7b019689dafd708f42abc8a6351b586b Mon Sep 17 00:00:00 2001 From: Xiang Xiao Date: Fri, 13 Mar 2026 16:33:37 +0800 Subject: [PATCH] testing/libc/arch_libc: Add tests for all string/memory functions. The arch_libc test only covered strcpy, so the architecture optimized implementations of the remaining string and memory routines were never exercised by the test suite. Extend the test to also cover memcpy, memmove, memset, memcmp, memchr, strlen, strcmp, strchr, strncmp, strnlen, strncpy, stpcpy, strcat and strrchr: * Every function gets a correctness test that sweeps the buffer alignment and the transfer size and compares the result against the expected value. * Every function gets a speed test that reports the average cycle count measured with perf_gettime(). * Every individual test is selected by its own CONFIG_TESTING_ARCH_LIBC_ option (default y), so a target can drop the ones it does not need. Impact: test only. Nothing is built unless CONFIG_TESTING_ARCH_LIBC (default n) is selected, so no existing board configuration changes. Testing: built and ran sim:nsh on Linux x86_64 (Ubuntu 24.04, gcc 13.3.0) with CONFIG_TESTING_ARCH_LIBC=y. All 15 enabled functions report PASSED and "arch_libc_test Passed". Signed-off-by: Xiang Xiao --- testing/libc/arch_libc/Kconfig | 56 + testing/libc/arch_libc/arch_libc_test_main.c | 1144 +++++++++++++++--- 2 files changed, 1056 insertions(+), 144 deletions(-) diff --git a/testing/libc/arch_libc/Kconfig b/testing/libc/arch_libc/Kconfig index 65927909c..b0aba69a5 100644 --- a/testing/libc/arch_libc/Kconfig +++ b/testing/libc/arch_libc/Kconfig @@ -12,10 +12,66 @@ config TESTING_ARCH_LIBC if TESTING_ARCH_LIBC +config TESTING_ARCH_LIBC_MEMCHR + bool "test memchr" + default y + +config TESTING_ARCH_LIBC_MEMCMP + bool "test memcmp" + default y + +config TESTING_ARCH_LIBC_MEMCPY + bool "test memcpy" + default y + +config TESTING_ARCH_LIBC_MEMMOVE + bool "test memmove" + default y + +config TESTING_ARCH_LIBC_MEMSET + bool "test memset" + default y + +config TESTING_ARCH_LIBC_STRCHR + bool "test strchr" + default y + +config TESTING_ARCH_LIBC_STRCMP + bool "test strcmp" + default y + config TESTING_ARCH_LIBC_STRCPY bool "test strcpy" default y +config TESTING_ARCH_LIBC_STRLEN + bool "test strlen" + default y + +config TESTING_ARCH_LIBC_STRNCMP + bool "test strncmp" + default y + +config TESTING_ARCH_LIBC_STRNLEN + bool "test strnlen" + default y + +config TESTING_ARCH_LIBC_STRNCPY + bool "test strncpy" + default y + +config TESTING_ARCH_LIBC_STPCPY + bool "test stpcpy" + default y + +config TESTING_ARCH_LIBC_STRCAT + bool "test strcat" + default y + +config TESTING_ARCH_LIBC_STRRCHR + bool "test strrchr" + default y + config TESTING_ARCH_LIBC_PROGNAME string "Program name" default "arch_libctest" diff --git a/testing/libc/arch_libc/arch_libc_test_main.c b/testing/libc/arch_libc/arch_libc_test_main.c index b69c1dfc4..67f8e6cb3 100644 --- a/testing/libc/arch_libc/arch_libc_test_main.c +++ b/testing/libc/arch_libc/arch_libc_test_main.c @@ -39,197 +39,994 @@ * Pre-processor Definitions ****************************************************************************/ -/* Configuration ************************************************************/ - -#ifdef CONFIG_TESTING_ARCH_LIBC_STRCPY -# define TEST_MAX_STRING_LEN 128 -# define TEST_SHORT_SRC_LEN 50 -# define TEST_LONG_SRC_LEN 128 -#endif +#define TEST_BUF_SIZE 256 +#define TEST_REPEAT 100 +#define MAX_ALIGN 16 /**************************************************************************** * Private Data ****************************************************************************/ -#ifdef CONFIG_TESTING_ARCH_LIBC_STRCPY -static char g_test_src_str[TEST_MAX_STRING_LEN]; -static char g_test_dst_str[TEST_MAX_STRING_LEN]; -#endif +static char g_buf1[TEST_BUF_SIZE + MAX_ALIGN]; +static char g_buf2[TEST_BUF_SIZE + MAX_ALIGN]; +static volatile uintptr_t g_sink; /**************************************************************************** * Private Functions ****************************************************************************/ -#ifdef CONFIG_TESTING_ARCH_LIBC_STRCPY - -/**************************************************************************** - * Name: arch_libc_test_strcpy - ****************************************************************************/ - -void init_short_test_str(int dst_offset, int src_offset) +static void fill_pattern(FAR char *buf, int len) { - int off; - - memset(g_test_src_str, '\0', sizeof(g_test_src_str)); - memset(g_test_dst_str, '\0', sizeof(g_test_dst_str)); - - for (off = 0; off < TEST_SHORT_SRC_LEN; off++) + int i; + for (i = 0; i < len; i++) { - g_test_src_str[off + src_offset] = '0' + off % 10; + buf[i] = 'A' + (i % 26); } } /**************************************************************************** - * Name: arch_libc_test_strcpy_offset + * Name: test_memcpy ****************************************************************************/ -int arch_libc_test_strcpy_offset(int dst_offset, int src_offset) +#ifdef CONFIG_TESTING_ARCH_LIBC_MEMCPY +static int test_memcpy(void) { - int off; - FAR char *dest; - FAR char *src; - FAR char *result_str; - bool pass = true; + int align; + int size; + int fail = 0; - init_short_test_str(dst_offset, src_offset); - dest = g_test_dst_str + dst_offset; - src = g_test_src_str + src_offset; - result_str = strcpy(dest, src); - - /* check strcpy data */ - - for (off = 0; off < TEST_SHORT_SRC_LEN; off++) + printf("Testing memcpy...\n"); + for (align = 0; align < 8; align++) { - if (result_str[off] != '0' + off % 10) + for (size = 1; size <= 128; size++) { - pass = false; - printf("dest copied data error, index %d\n", off); - } - - if (src[off] != '0' + off % 10) - { - pass = false; - printf("src data error, index %d\n", off); - } - } - - for (off = TEST_SHORT_SRC_LEN; off < TEST_MAX_STRING_LEN - dst_offset; - off++) - { - if (result_str[off] != '\0') - { - pass = false; - printf("dest tailing zero error, index %d\n", off); - } - } - - for (off = TEST_SHORT_SRC_LEN; off < TEST_MAX_STRING_LEN - src_offset; - off++) - { - if (src[off] != '\0') - { - pass = false; - printf("src tailing zero error, index %d\n", off); - } - } - - /* strcpy shouldn't change arch_libc_test_strcpy's local variable */ - - if (dest != (g_test_dst_str + dst_offset) || - src != (g_test_src_str + src_offset)) - { - pass = false; - printf("local var changed after calling strcpy\n"); - } - - if (!pass) - { - printf("Test Failed at dst/src offset [%d, %d]\n", dst_offset, - src_offset); - return -1; - } - - return 0; -} - -/**************************************************************************** - * Name: arch_libc_test_strcpy - ****************************************************************************/ - -int arch_libc_test_strcpy(void) -{ - int dest_off; - int src_off; - int ret = 0; - - for (dest_off = 0; dest_off <= 4; dest_off++) - { - for (src_off = 0; src_off <= 4; src_off++) - { - if (arch_libc_test_strcpy_offset(dest_off, src_off) != 0) + fill_pattern(g_buf1 + align, size); + memset(g_buf2, 0, sizeof(g_buf2)); + memcpy(g_buf2 + align, g_buf1 + align, size); + if (memcmp(g_buf2 + align, g_buf1 + align, size) != 0) { - ret = -1; + printf(" FAIL: align=%d size=%d\n", align, size); + fail++; } } } - if (ret != 0) - { - printf("arch_libc_test_strcpy Test Failed\n"); - } - else - { - printf("arch_libc_test_strcpy Test Passed\n"); - } - - return ret; + printf("memcpy: %s\n", fail ? "FAILED" : "PASSED"); + return fail; } -/**************************************************************************** - * Name: arch_libc_strcpy_speed_offset - ****************************************************************************/ - -clock_t arch_libc_strcpy_speed_offset(int dst_offset, int src_offset) +static void speed_memcpy(void) { - FAR char *dest; - FAR char *src; clock_t start; clock_t end; + int i; - init_short_test_str(dst_offset, src_offset); - dest = g_test_dst_str + dst_offset; - src = g_test_src_str + src_offset; + fill_pattern(g_buf1, 128); start = perf_gettime(); - strcpy(dest, src); - end = perf_gettime(); + for (i = 0; i < TEST_REPEAT; i++) + { + g_sink = (uintptr_t)memcpy(g_buf2, g_buf1, 128); + } - return end - start; + end = perf_gettime(); + printf("memcpy(128) avg cycles: %ju\n", + (uintmax_t)(end - start) / TEST_REPEAT); } +#endif /**************************************************************************** - * Name: arch_libc_strcpy_speed + * Name: test_memmove ****************************************************************************/ -int arch_libc_strcpy_speed(void) +#ifdef CONFIG_TESTING_ARCH_LIBC_MEMMOVE +static int test_memmove(void) { - int dest_off; - int src_off; - clock_t cycles = 0; + int size; + int fail = 0; - for (dest_off = 0; dest_off <= 4; dest_off++) + printf("Testing memmove...\n"); + + /* Test overlapping forward */ + + for (size = 1; size <= 64; size++) { - for (src_off = 0; src_off <= 4; src_off++) + fill_pattern(g_buf1, size + 16); + memcpy(g_buf2, g_buf1, size + 16); + memmove(g_buf1 + 8, g_buf1, size); + if (memcmp(g_buf1 + 8, g_buf2, size) != 0) { - cycles += arch_libc_strcpy_speed_offset(dest_off, src_off); + printf(" FAIL forward: size=%d\n", size); + fail++; } } - printf("strcpy total(run 25 times) cpu cycles %"PRIu64"\n", cycles); - printf("strcpy average cpu cycles %"PRIu64"\n", cycles / 25); + /* Test overlapping backward */ - return 0; + for (size = 1; size <= 64; size++) + { + fill_pattern(g_buf1 + 8, size); + memcpy(g_buf2, g_buf1 + 8, size); + memmove(g_buf1, g_buf1 + 8, size); + if (memcmp(g_buf1, g_buf2, size) != 0) + { + printf(" FAIL backward: size=%d\n", size); + fail++; + } + } + + printf("memmove: %s\n", fail ? "FAILED" : "PASSED"); + return fail; } +static void speed_memmove(void) +{ + clock_t start; + clock_t end; + int i; + + fill_pattern(g_buf1, 128); + start = perf_gettime(); + for (i = 0; i < TEST_REPEAT; i++) + { + g_sink = (uintptr_t)memmove(g_buf2, g_buf1, 128); + } + + end = perf_gettime(); + printf("memmove(128) avg cycles: %ju\n", + (uintmax_t)(end - start) / TEST_REPEAT); +} +#endif + +/**************************************************************************** + * Name: test_memset + ****************************************************************************/ + +#ifdef CONFIG_TESTING_ARCH_LIBC_MEMSET +static int test_memset(void) +{ + int align; + int size; + int i; + int fail = 0; + + printf("Testing memset...\n"); + for (align = 0; align < 8; align++) + { + for (size = 1; size <= 128; size++) + { + memset(g_buf1, 0, sizeof(g_buf1)); + memset(g_buf1 + align, 0xaa, size); + for (i = 0; i < size; i++) + { + if ((unsigned char)g_buf1[align + i] != 0xaa) + { + printf(" FAIL: align=%d size=%d idx=%d\n", + align, size, i); + fail++; + break; + } + } + } + } + + printf("memset: %s\n", fail ? "FAILED" : "PASSED"); + return fail; +} + +static void speed_memset(void) +{ + clock_t start; + clock_t end; + int i; + + start = perf_gettime(); + for (i = 0; i < TEST_REPEAT; i++) + { + g_sink = (uintptr_t)memset(g_buf1, 0x55, 128); + } + + end = perf_gettime(); + printf("memset(128) avg cycles: %ju\n", + (uintmax_t)(end - start) / TEST_REPEAT); +} +#endif + +/**************************************************************************** + * Name: test_memcmp + ****************************************************************************/ + +#ifdef CONFIG_TESTING_ARCH_LIBC_MEMCMP +static int test_memcmp(void) +{ + int size; + int fail = 0; + + printf("Testing memcmp...\n"); + for (size = 1; size <= 128; size++) + { + fill_pattern(g_buf1, size); + fill_pattern(g_buf2, size); + if (memcmp(g_buf1, g_buf2, size) != 0) + { + printf(" FAIL equal: size=%d\n", size); + fail++; + } + + g_buf2[size - 1] = '~'; + if (memcmp(g_buf1, g_buf2, size) >= 0) + { + printf(" FAIL less: size=%d\n", size); + fail++; + } + + g_buf2[size - 1] = 0; + if (memcmp(g_buf1, g_buf2, size) <= 0) + { + printf(" FAIL greater: size=%d\n", size); + fail++; + } + } + + printf("memcmp: %s\n", fail ? "FAILED" : "PASSED"); + return fail; +} + +static void speed_memcmp(void) +{ + clock_t start; + clock_t end; + int i; + + fill_pattern(g_buf1, 128); + fill_pattern(g_buf2, 128); + start = perf_gettime(); + for (i = 0; i < TEST_REPEAT; i++) + { + g_sink = (uintptr_t)memcmp(g_buf1, g_buf2, 128); + } + + end = perf_gettime(); + printf("memcmp(128) avg cycles: %ju\n", + (uintmax_t)(end - start) / TEST_REPEAT); +} +#endif + +/**************************************************************************** + * Name: test_memchr + ****************************************************************************/ + +#ifdef CONFIG_TESTING_ARCH_LIBC_MEMCHR +static int test_memchr(void) +{ + int size; + int fail = 0; + FAR void *p; + + printf("Testing memchr...\n"); + for (size = 1; size <= 128; size++) + { + fill_pattern(g_buf1, size); + + /* Find last char */ + + p = memchr(g_buf1, g_buf1[size - 1], size); + if (p == NULL) + { + printf(" FAIL found: size=%d\n", size); + fail++; + } + + /* Not found */ + + p = memchr(g_buf1, 0xff, size); + if (p != NULL) + { + printf(" FAIL notfound: size=%d\n", size); + fail++; + } + } + + printf("memchr: %s\n", fail ? "FAILED" : "PASSED"); + return fail; +} + +static void speed_memchr(void) +{ + clock_t start; + clock_t end; + int i; + + fill_pattern(g_buf1, 128); + start = perf_gettime(); + for (i = 0; i < TEST_REPEAT; i++) + { + g_sink = (uintptr_t)memchr(g_buf1, g_buf1[127], 128); + } + + end = perf_gettime(); + printf("memchr(128) avg cycles: %ju\n", + (uintmax_t)(end - start) / TEST_REPEAT); +} +#endif + +/**************************************************************************** + * Name: test_strlen + ****************************************************************************/ + +#ifdef CONFIG_TESTING_ARCH_LIBC_STRLEN +static int test_strlen(void) +{ + int size; + int fail = 0; + + printf("Testing strlen...\n"); + for (size = 0; size <= 128; size++) + { + fill_pattern(g_buf1, size); + g_buf1[size] = '\0'; + if ((int)strlen(g_buf1) != size) + { + printf(" FAIL: size=%d got=%d\n", size, (int)strlen(g_buf1)); + fail++; + } + } + + printf("strlen: %s\n", fail ? "FAILED" : "PASSED"); + return fail; +} + +static void speed_strlen(void) +{ + clock_t start; + clock_t end; + int i; + + fill_pattern(g_buf1, 128); + g_buf1[128] = '\0'; + start = perf_gettime(); + for (i = 0; i < TEST_REPEAT; i++) + { + g_sink = strlen(g_buf1); + } + + end = perf_gettime(); + printf("strlen(128) avg cycles: %ju\n", + (uintmax_t)(end - start) / TEST_REPEAT); +} +#endif + +/**************************************************************************** + * Name: test_strcmp + ****************************************************************************/ + +#ifdef CONFIG_TESTING_ARCH_LIBC_STRCMP +static int test_strcmp(void) +{ + int size; + int fail = 0; + + printf("Testing strcmp...\n"); + for (size = 1; size <= 128; size++) + { + fill_pattern(g_buf1, size); + g_buf1[size] = '\0'; + fill_pattern(g_buf2, size); + g_buf2[size] = '\0'; + if (strcmp(g_buf1, g_buf2) != 0) + { + printf(" FAIL equal: size=%d\n", size); + fail++; + } + + g_buf2[size - 1] = '~'; + g_buf2[size] = '\0'; + if (strcmp(g_buf1, g_buf2) >= 0) + { + printf(" FAIL less: size=%d\n", size); + fail++; + } + } + + printf("strcmp: %s\n", fail ? "FAILED" : "PASSED"); + return fail; +} + +static void speed_strcmp(void) +{ + clock_t start; + clock_t end; + int i; + + fill_pattern(g_buf1, 128); + g_buf1[128] = '\0'; + fill_pattern(g_buf2, 128); + g_buf2[128] = '\0'; + start = perf_gettime(); + for (i = 0; i < TEST_REPEAT; i++) + { + g_sink = strcmp(g_buf1, g_buf2); + } + + end = perf_gettime(); + printf("strcmp(128) avg cycles: %ju\n", + (uintmax_t)(end - start) / TEST_REPEAT); +} +#endif + +/**************************************************************************** + * Name: test_strcpy + ****************************************************************************/ + +#ifdef CONFIG_TESTING_ARCH_LIBC_STRCPY +static int test_strcpy(void) +{ + int align; + int size; + int fail = 0; + + printf("Testing strcpy...\n"); + for (align = 0; align < 8; align++) + { + for (size = 1; size <= 64; size++) + { + fill_pattern(g_buf1 + align, size); + g_buf1[align + size] = '\0'; + memset(g_buf2, 0, sizeof(g_buf2)); + strcpy(g_buf2 + align, g_buf1 + align); + if (strcmp(g_buf2 + align, g_buf1 + align) != 0) + { + printf(" FAIL: align=%d size=%d\n", align, size); + fail++; + } + } + } + + printf("strcpy: %s\n", fail ? "FAILED" : "PASSED"); + return fail; +} + +static void speed_strcpy(void) +{ + clock_t start; + clock_t end; + int i; + + fill_pattern(g_buf1, 128); + g_buf1[128] = '\0'; + start = perf_gettime(); + for (i = 0; i < TEST_REPEAT; i++) + { + g_sink = (uintptr_t)strcpy(g_buf2, g_buf1); + } + + end = perf_gettime(); + printf("strcpy(128) avg cycles: %ju\n", + (uintmax_t)(end - start) / TEST_REPEAT); +} +#endif + +/**************************************************************************** + * Name: test_strchr + ****************************************************************************/ + +#ifdef CONFIG_TESTING_ARCH_LIBC_STRCHR +static int test_strchr(void) +{ + int size; + int fail = 0; + FAR char *p; + + printf("Testing strchr...\n"); + for (size = 1; size <= 128; size++) + { + fill_pattern(g_buf1, size); + g_buf1[size] = '\0'; + + /* Find first char */ + + p = strchr(g_buf1, g_buf1[0]); + if (p != g_buf1) + { + printf(" FAIL first: size=%d\n", size); + fail++; + } + + /* Find NUL */ + + p = strchr(g_buf1, '\0'); + if (p != g_buf1 + size) + { + printf(" FAIL nul: size=%d\n", size); + fail++; + } + + /* Not found */ + + p = strchr(g_buf1, 0x01); + if (p != NULL) + { + printf(" FAIL notfound: size=%d\n", size); + fail++; + } + } + + printf("strchr: %s\n", fail ? "FAILED" : "PASSED"); + return fail; +} + +static void speed_strchr(void) +{ + clock_t start; + clock_t end; + int i; + + fill_pattern(g_buf1, 128); + g_buf1[128] = '\0'; + start = perf_gettime(); + for (i = 0; i < TEST_REPEAT; i++) + { + g_sink = (uintptr_t)strchr(g_buf1, g_buf1[127]); + } + + end = perf_gettime(); + printf("strchr(128) avg cycles: %ju\n", + (uintmax_t)(end - start) / TEST_REPEAT); +} +#endif + +/**************************************************************************** + * Name: test_strncmp + ****************************************************************************/ + +#ifdef CONFIG_TESTING_ARCH_LIBC_STRNCMP +static int test_strncmp(void) +{ + int size; + int fail = 0; + + printf("Testing strncmp...\n"); + for (size = 1; size <= 128; size++) + { + fill_pattern(g_buf1, size); + g_buf1[size] = '\0'; + fill_pattern(g_buf2, size); + g_buf2[size] = '\0'; + + /* Equal within n */ + + if (strncmp(g_buf1, g_buf2, size) != 0) + { + printf(" FAIL equal: size=%d\n", size); + fail++; + } + + /* Differ at last position */ + + g_buf2[size - 1] = '~'; + if (strncmp(g_buf1, g_buf2, size) >= 0) + { + printf(" FAIL less: size=%d\n", size); + fail++; + } + + /* Equal when n is smaller */ + + fill_pattern(g_buf2, size); + g_buf2[size] = '\0'; + g_buf2[size - 1] = '~'; + if (size > 1 && strncmp(g_buf1, g_buf2, size - 1) != 0) + { + printf(" FAIL partial: size=%d\n", size); + fail++; + } + + /* Zero count */ + + if (strncmp(g_buf1, g_buf2, 0) != 0) + { + printf(" FAIL zero: size=%d\n", size); + fail++; + } + } + + printf("strncmp: %s\n", fail ? "FAILED" : "PASSED"); + return fail; +} + +static void speed_strncmp(void) +{ + clock_t start; + clock_t end; + int i; + + fill_pattern(g_buf1, 128); + g_buf1[128] = '\0'; + fill_pattern(g_buf2, 128); + g_buf2[128] = '\0'; + start = perf_gettime(); + for (i = 0; i < TEST_REPEAT; i++) + { + g_sink = strncmp(g_buf1, g_buf2, 128); + } + + end = perf_gettime(); + printf("strncmp(128) avg cycles: %ju\n", + (uintmax_t)(end - start) / TEST_REPEAT); +} +#endif + +/**************************************************************************** + * Name: test_strnlen + ****************************************************************************/ + +#ifdef CONFIG_TESTING_ARCH_LIBC_STRNLEN +static int test_strnlen(void) +{ + int size; + int fail = 0; + + printf("Testing strnlen...\n"); + for (size = 0; size <= 128; size++) + { + fill_pattern(g_buf1, size); + g_buf1[size] = '\0'; + + /* maxlen >= actual length */ + + if ((int)strnlen(g_buf1, size + 10) != size) + { + printf(" FAIL full: size=%d\n", size); + fail++; + } + + /* maxlen < actual length */ + + if (size > 0 && (int)strnlen(g_buf1, size - 1) != size - 1) + { + printf(" FAIL trunc: size=%d\n", size); + fail++; + } + + /* maxlen == 0 */ + + if (strnlen(g_buf1, 0) != 0) + { + printf(" FAIL zero: size=%d\n", size); + fail++; + } + } + + printf("strnlen: %s\n", fail ? "FAILED" : "PASSED"); + return fail; +} + +static void speed_strnlen(void) +{ + clock_t start; + clock_t end; + int i; + + fill_pattern(g_buf1, 128); + g_buf1[128] = '\0'; + start = perf_gettime(); + for (i = 0; i < TEST_REPEAT; i++) + { + g_sink = strnlen(g_buf1, 256); + } + + end = perf_gettime(); + printf("strnlen(128) avg cycles: %ju\n", + (uintmax_t)(end - start) / TEST_REPEAT); +} +#endif + +/**************************************************************************** + * Name: test_strncpy + ****************************************************************************/ + +#ifdef CONFIG_TESTING_ARCH_LIBC_STRNCPY +static int test_strncpy(void) +{ + int align; + int size; + int i; + int fail = 0; + + printf("Testing strncpy...\n"); + for (align = 0; align < 8; align++) + { + for (size = 1; size <= 64; size++) + { + fill_pattern(g_buf1 + align, size); + g_buf1[align + size] = '\0'; + + /* n > strlen: should copy and zero-fill */ + + memset(g_buf2, 0xaa, sizeof(g_buf2)); + strncpy(g_buf2 + align, g_buf1 + align, size + 4); + if (strcmp(g_buf2 + align, g_buf1 + align) != 0) + { + printf(" FAIL copy: align=%d size=%d\n", align, size); + fail++; + } + + /* Check zero-fill */ + + for (i = 0; i < 4; i++) + { + if (g_buf2[align + size + i] != '\0') + { + printf(" FAIL zfill: align=%d size=%d\n", align, size); + fail++; + break; + } + } + + /* n < strlen: should truncate without NUL */ + + if (size > 1) + { + memset(g_buf2, 0xaa, sizeof(g_buf2)); + strncpy(g_buf2 + align, g_buf1 + align, size - 1); + if (memcmp(g_buf2 + align, g_buf1 + align, size - 1) != 0) + { + printf(" FAIL trunc: align=%d size=%d\n", align, size); + fail++; + } + } + } + } + + printf("strncpy: %s\n", fail ? "FAILED" : "PASSED"); + return fail; +} + +static void speed_strncpy(void) +{ + clock_t start; + clock_t end; + int i; + + fill_pattern(g_buf1, 128); + g_buf1[128] = '\0'; + start = perf_gettime(); + for (i = 0; i < TEST_REPEAT; i++) + { + g_sink = (uintptr_t)strncpy(g_buf2, g_buf1, 128); + } + + end = perf_gettime(); + printf("strncpy(128) avg cycles: %ju\n", + (uintmax_t)(end - start) / TEST_REPEAT); +} +#endif + +/**************************************************************************** + * Name: test_stpcpy + ****************************************************************************/ + +#ifdef CONFIG_TESTING_ARCH_LIBC_STPCPY +static int test_stpcpy(void) +{ + int align; + int size; + int fail = 0; + FAR char *p; + + printf("Testing stpcpy...\n"); + for (align = 0; align < 8; align++) + { + for (size = 1; size <= 64; size++) + { + fill_pattern(g_buf1 + align, size); + g_buf1[align + size] = '\0'; + memset(g_buf2, 0, sizeof(g_buf2)); + p = stpcpy(g_buf2 + align, g_buf1 + align); + + /* Check content */ + + if (strcmp(g_buf2 + align, g_buf1 + align) != 0) + { + printf(" FAIL copy: align=%d size=%d\n", align, size); + fail++; + } + + /* Check return value points to NUL */ + + if (p != g_buf2 + align + size) + { + printf(" FAIL retval: align=%d size=%d\n", align, size); + fail++; + } + + if (*p != '\0') + { + printf(" FAIL nul: align=%d size=%d\n", align, size); + fail++; + } + } + } + + printf("stpcpy: %s\n", fail ? "FAILED" : "PASSED"); + return fail; +} + +static void speed_stpcpy(void) +{ + clock_t start; + clock_t end; + int i; + + fill_pattern(g_buf1, 128); + g_buf1[128] = '\0'; + start = perf_gettime(); + for (i = 0; i < TEST_REPEAT; i++) + { + g_sink = (uintptr_t)stpcpy(g_buf2, g_buf1); + } + + end = perf_gettime(); + printf("stpcpy(128) avg cycles: %ju\n", + (uintmax_t)(end - start) / TEST_REPEAT); +} +#endif + +/**************************************************************************** + * Name: test_strcat + ****************************************************************************/ + +#ifdef CONFIG_TESTING_ARCH_LIBC_STRCAT +static int test_strcat(void) +{ + int size; + int fail = 0; + + printf("Testing strcat...\n"); + for (size = 1; size <= 64; size++) + { + /* Build expected: "ABCD..." + "ABCD..." */ + + fill_pattern(g_buf1, size); + g_buf1[size] = '\0'; + + memset(g_buf2, 0, sizeof(g_buf2)); + fill_pattern(g_buf2, size); + g_buf2[size] = '\0'; + + strcat(g_buf2, g_buf1); + + /* Check total length */ + + if ((int)strlen(g_buf2) != size * 2) + { + printf(" FAIL len: size=%d got=%d\n", size, + (int)strlen(g_buf2)); + fail++; + } + + /* Check second half matches */ + + if (memcmp(g_buf2 + size, g_buf1, size) != 0) + { + printf(" FAIL content: size=%d\n", size); + fail++; + } + + /* Test cat to empty string */ + + g_buf2[0] = '\0'; + strcat(g_buf2, g_buf1); + if (strcmp(g_buf2, g_buf1) != 0) + { + printf(" FAIL empty: size=%d\n", size); + fail++; + } + } + + printf("strcat: %s\n", fail ? "FAILED" : "PASSED"); + return fail; +} + +static void speed_strcat(void) +{ + clock_t start; + clock_t end; + int i; + + fill_pattern(g_buf1, 64); + g_buf1[64] = '\0'; + start = perf_gettime(); + for (i = 0; i < TEST_REPEAT; i++) + { + g_buf2[0] = '\0'; + g_sink = (uintptr_t)strcat(g_buf2, g_buf1); + } + + end = perf_gettime(); + printf("strcat(64) avg cycles: %ju\n", + (uintmax_t)(end - start) / TEST_REPEAT); +} +#endif + +/**************************************************************************** + * Name: test_strrchr + ****************************************************************************/ + +#ifdef CONFIG_TESTING_ARCH_LIBC_STRRCHR +static int test_strrchr(void) +{ + int size; + int fail = 0; + FAR char *p; + + printf("Testing strrchr...\n"); + for (size = 1; size <= 128; size++) + { + fill_pattern(g_buf1, size); + g_buf1[size] = '\0'; + + /* Find last occurrence of first char (repeats every 26) */ + + p = strrchr(g_buf1, 'A'); + if (p == NULL) + { + printf(" FAIL found: size=%d\n", size); + fail++; + } + else + { + /* 'A' appears at 0, 26, 52, ... — last one <= size-1 */ + + int expected = ((size - 1) / 26) * 26; + if (p != g_buf1 + expected) + { + printf(" FAIL pos: size=%d expected=%d got=%d\n", + size, expected, (int)(p - g_buf1)); + fail++; + } + } + + /* Find NUL */ + + p = strrchr(g_buf1, '\0'); + if (p != g_buf1 + size) + { + printf(" FAIL nul: size=%d\n", size); + fail++; + } + + /* Not found */ + + p = strrchr(g_buf1, 0x01); + if (p != NULL) + { + printf(" FAIL notfound: size=%d\n", size); + fail++; + } + } + + printf("strrchr: %s\n", fail ? "FAILED" : "PASSED"); + return fail; +} + +static void speed_strrchr(void) +{ + clock_t start; + clock_t end; + int i; + + fill_pattern(g_buf1, 128); + g_buf1[128] = '\0'; + start = perf_gettime(); + for (i = 0; i < TEST_REPEAT; i++) + { + g_sink = (uintptr_t)strrchr(g_buf1, g_buf1[127]); + } + + end = perf_gettime(); + printf("strrchr(128) avg cycles: %ju\n", + (uintmax_t)(end - start) / TEST_REPEAT); +} #endif /**************************************************************************** @@ -242,11 +1039,70 @@ int arch_libc_strcpy_speed(void) int main(int argc, FAR char *argv[]) { + int fail = 0; + +#ifdef CONFIG_TESTING_ARCH_LIBC_MEMCPY + fail += test_memcpy(); + speed_memcpy(); +#endif +#ifdef CONFIG_TESTING_ARCH_LIBC_MEMMOVE + fail += test_memmove(); + speed_memmove(); +#endif +#ifdef CONFIG_TESTING_ARCH_LIBC_MEMSET + fail += test_memset(); + speed_memset(); +#endif +#ifdef CONFIG_TESTING_ARCH_LIBC_MEMCMP + fail += test_memcmp(); + speed_memcmp(); +#endif +#ifdef CONFIG_TESTING_ARCH_LIBC_MEMCHR + fail += test_memchr(); + speed_memchr(); +#endif +#ifdef CONFIG_TESTING_ARCH_LIBC_STRLEN + fail += test_strlen(); + speed_strlen(); +#endif +#ifdef CONFIG_TESTING_ARCH_LIBC_STRCMP + fail += test_strcmp(); + speed_strcmp(); +#endif #ifdef CONFIG_TESTING_ARCH_LIBC_STRCPY - arch_libc_test_strcpy(); - arch_libc_strcpy_speed(); + fail += test_strcpy(); + speed_strcpy(); +#endif +#ifdef CONFIG_TESTING_ARCH_LIBC_STRCHR + fail += test_strchr(); + speed_strchr(); +#endif +#ifdef CONFIG_TESTING_ARCH_LIBC_STRNCMP + fail += test_strncmp(); + speed_strncmp(); +#endif +#ifdef CONFIG_TESTING_ARCH_LIBC_STRNLEN + fail += test_strnlen(); + speed_strnlen(); +#endif +#ifdef CONFIG_TESTING_ARCH_LIBC_STRNCPY + fail += test_strncpy(); + speed_strncpy(); +#endif +#ifdef CONFIG_TESTING_ARCH_LIBC_STPCPY + fail += test_stpcpy(); + speed_stpcpy(); +#endif +#ifdef CONFIG_TESTING_ARCH_LIBC_STRCAT + fail += test_strcat(); + speed_strcat(); +#endif +#ifdef CONFIG_TESTING_ARCH_LIBC_STRRCHR + fail += test_strrchr(); + speed_strrchr(); #endif - return 0; + printf("arch_libc_test %s\n", fail ? "Failed" : "Passed"); + return fail ? EXIT_FAILURE : EXIT_SUCCESS; }