diff --git a/src/libc/include/string.h b/src/libc/include/string.h index 67cc527ec..850b9908a 100644 --- a/src/libc/include/string.h +++ b/src/libc/include/string.h @@ -86,6 +86,9 @@ char *strcasestr(const char *haystack, const char *needle) char *strtok(char *__restrict s, const char *__restrict delim) __attribute__((nonnull(2))); +char *strtok_r(char *__restrict s, const char *__restrict delim, char **__restrict save_ptr) + __attribute__((nonnull(2, 3))); + char *strdup(const char *s) __attribute__((__malloc__)) __attribute__((nonnull(1))); diff --git a/src/libc/memchr.src b/src/libc/memchr.src new file mode 100644 index 000000000..08330d4bd --- /dev/null +++ b/src/libc/memchr.src @@ -0,0 +1,32 @@ + .assume adl=1 + + .section .text + + .global _memchr + .type _memchr, @function + +.ifdef PREFER_CE_LIBC + + .set _memchr, 0x00009C + +.else + +_memchr: + ld iy, -1 + add iy, sp + ld bc, (iy + 10) + sbc hl, hl + add hl, bc + jr nc, .L.ret_zero + ld hl, (iy + 4) + ld a, (iy + 7) + cpir + dec hl + ret z ; found match + or a, a +.L.ret_zero: + ; return NULL + sbc hl, hl + ret + +.endif diff --git a/src/libc/memcmp.src b/src/libc/memcmp.src new file mode 100644 index 000000000..71c0fc60f --- /dev/null +++ b/src/libc/memcmp.src @@ -0,0 +1,42 @@ + .assume adl=1 + + .section .text + + .global _memcmp + .type _memcmp, @function + +.ifdef PREFER_CE_LIBC + + .set _memcmp, 0x0000A0 + +.else + +_memcmp: + ld iy, 0 + add iy, sp + ld bc, (iy + 9) + sbc hl, hl + adc hl, bc + ld hl, (iy + 6) + ld de, (iy + 3) + call nz, .L.start ; z means BC was zero +.L.ret_zero: + sbc hl, hl + ret + +.L.loop: + ret po + inc de +.L.start: + ld a, (de) + cpi + jr z, .L.loop +.L.finish: + ld sp, iy ; ret + dec hl + sub a, (hl) + sbc hl, hl + ld l, a + ret + +.endif diff --git a/src/libc/os.src b/src/libc/os.src index c1df4c8a4..07c62e7d6 100644 --- a/src/libc/os.src +++ b/src/libc/os.src @@ -4,33 +4,6 @@ .global _longjmp .type _longjmp, @function .set _longjmp, 0x000098 - .global _memchr - .type _memchr, @function - .set _memchr, 0x00009C - .global _memcmp - .type _memcmp, @function - .set _memcmp, 0x0000A0 .global _setjmp .type _setjmp, @function .set _setjmp, 0x0000B8 - .global _strcat - .type _strcat, @function - .set _strcat, 0x0000C0 - .global _strchr - .type _strchr, @function - .set _strchr, 0x0000C4 - .global _strcpy - .type _strcpy, @function - .set _strcpy, 0x0000CC - .global _strncat - .type _strncat, @function - .set _strncat, 0x0000D8 - .global _strncpy - .type _strncpy, @function - .set _strncpy, 0x0000E0 - .global _strstr - .type _strstr, @function - .set _strstr, 0x0000F0 - .global _strtok - .type _strtok, @function - .set _strtok, 0x0000F4 diff --git a/src/libc/strcat.src b/src/libc/strcat.src new file mode 100644 index 000000000..8249fabda --- /dev/null +++ b/src/libc/strcat.src @@ -0,0 +1,46 @@ + .assume adl=1 + + .section .text + + .global _strcat + .type _strcat, @function + +.ifdef PREFER_CE_LIBC + + .set _strcat, 0x0000C0 + +.else + +_strcat: + pop iy + pop de + ex (sp), hl ; src + push de ; dst + + ; move to the end of src + xor a, a + ld bc, 0 + push hl ; src + cpir + sbc hl, hl + sbc hl, bc ; carry is set + ex (sp), hl + ex de, hl + ; move to the end of dst + + ; not needed unless the strlen(dst) + strlen(src) > 16777215 bytes, + ; which would imply that the resulting string would not fit into memory + ; ld bc, 0 + cpir + dec hl + + pop bc + ex de, hl + ; DE = dst + strlen(dst) + ; HL = src + ; BC = strlen(src) + 1 + ldir + ex (sp), hl ; return value + jp (iy) + +.endif diff --git a/src/libc/strchr.src b/src/libc/strchr.src new file mode 100644 index 000000000..9691fb33f --- /dev/null +++ b/src/libc/strchr.src @@ -0,0 +1,42 @@ + .assume adl=1 + + .section .text + + .global _strchr + .type _strchr, @function + +.ifdef PREFER_CE_LIBC + + .set _strchr, 0x0000C4 + +.else + +_strchr: + pop bc + pop hl + pop de + push de + push hl + push bc + xor a, a + ld bc, 0 + cpir + ld a, e + ex de, hl + ; calculate strlen(str) + 1 + sbc hl, hl + sbc hl, bc + ex de, hl + ; rewind HL to the beginning of str + add hl, bc + push de + pop bc + cpir + dec hl + ret z ; found match + ; return NULL + or a, a + sbc hl, hl + ret + +.endif diff --git a/src/libc/strcpy.src b/src/libc/strcpy.src new file mode 100644 index 000000000..7a3b7eeae --- /dev/null +++ b/src/libc/strcpy.src @@ -0,0 +1,37 @@ + .assume adl=1 + + .section .text + + .global _strcpy + .type _strcpy, @function + +.ifdef PREFER_CE_LIBC + + .set _strcpy, 0x0000CC + +.else + +_strcpy: + pop iy + pop de + ex (sp), hl ; src + push de ; dst + + xor a, a + ld bc, 0 + ; move to the end of src + push hl + cpir + sbc hl, hl + sbc hl, bc + ex (sp), hl + pop bc + + ; DE = dst + ; HL = src + ; BC = strlen(src) + 1 + ldir + ex (sp), hl ; return dst + jp (iy) + +.endif diff --git a/src/libc/strncat.src b/src/libc/strncat.src new file mode 100644 index 000000000..96150994a --- /dev/null +++ b/src/libc/strncat.src @@ -0,0 +1,51 @@ + .assume adl=1 + + .section .text + + .global _strncat + .type _strncat, @function + +.ifdef PREFER_OS_LIBC + + .set _strncat, 0x0000D8 + +.else + +_strncat: + ld iy, 0 + lea bc, iy + 0 + add iy, sp + xor a, a + ld hl, (iy + 3) ; dst + cpir + dec hl + push hl ; dst + strlen(dst) + + ; inlined strnlen + ld bc, (iy + 9) ; max_size + sbc hl, hl + sbc hl, bc + jr z, .L.zero_size + add hl, bc + ld de, (iy + 6) ; src + sbc hl, de + ex de, hl + cpir + jr z, .L.finish_strnlen + inc hl +.L.finish_strnlen: + xor a, a + adc hl, de +.L.zero_size: + pop de ; dst + strlen(dst) + jr z, .L.no_room + push hl + pop bc + ld hl, (iy + 6) ; src + ldir + ld (de), a ; nul terminate +.L.no_room: + ld hl, (iy + 3) + ret + +.endif diff --git a/src/libc/strncpy.src b/src/libc/strncpy.src new file mode 100644 index 000000000..6102fdebc --- /dev/null +++ b/src/libc/strncpy.src @@ -0,0 +1,69 @@ + .assume adl=1 + + .section .text + + .global _strncpy + .type _strncpy, @function + +.ifdef PREFER_CE_LIBC + + .set _strncpy, 0x0000E0 + +.else + +; currently this just copies the stpncpy implementation, and could probably be optimized further +_strncpy: + ld iy, 0 + add iy, sp + ld bc, (iy + 9) ; max_len + + ; inlined strnlen + xor a, a + sbc hl, hl + sbc hl, bc + jr z, .L.zero_size + add hl, bc + ld de, (iy + 6) ; src + sbc hl, de + ex de, hl + cpir + jr z, .L.finish_strnlen + inc hl +.L.finish_strnlen: + xor a, a + adc hl, de +.L.zero_size: + + ; copy strnlen bytes from src + push hl + ld de, (iy + 3) ; dst + jr z, .L.zero_byte_copy + ld hl, (iy + 6) ; src + pop bc + push bc + ldir +.L.zero_byte_copy: + pop bc + + ; zero pad the remainder + ld hl, (iy + 9) ; max_len + scf + sbc hl, bc ; clear_size - 1 = max_len - src_len - 1 + ex de, hl + jr c, .L.finish ; clear_size <= 0 (or max_len <= src_len) + ; HL = dst + src_len + ; DE = clear_size - 1 + add hl, de + ld (hl), a + jr z, .L.finish ; clear_size == 1 + push de + pop bc + push hl + pop de + dec de + lddr +.L.finish: + ld hl, (iy + 3) + ret + +.endif diff --git a/src/libc/strstr.src b/src/libc/strstr.src new file mode 100644 index 000000000..74be8af41 --- /dev/null +++ b/src/libc/strstr.src @@ -0,0 +1,51 @@ + .assume adl=1 + + .section .text + + .global _strstr + .type _strstr, @function + +.ifdef PREFER_CE_LIBC + + .set _strstr, 0x0000F0 + +.else + +; strstr can probably be optimized further beyond just wrapping memmem +_strstr: + pop bc + pop de + ex (sp), hl + push de + push bc + + push hl + xor a, a + ld bc, 0 + cpir + sbc hl, hl + dec hl + sbc hl, bc ; carry will be cleared + ex (sp), hl ; strlen(needle) + push hl ; needle + + ex de, hl + push hl + ld bc, 0 + cpir + sbc hl, hl + dec hl + sbc hl, bc + ex (sp), hl ; strlen(haystack) + push hl ; haystack + + call _memmem + pop bc + pop bc + pop bc + pop bc + ret + + .extern _memmem + +.endif diff --git a/src/libc/strtok.src b/src/libc/strtok.src new file mode 100644 index 000000000..a08ff1627 --- /dev/null +++ b/src/libc/strtok.src @@ -0,0 +1,103 @@ + .assume adl=1 + +;------------------------------------------------------------------------------- + + .section .text + + .global _strtok + .type _strtok, @function + +.ifdef PREFER_CE_LIBC + + .set _strtok, 0x0000F4 + +.else + + .equ strtokPtr, $D000FF + +; strtok_r(char *__restrict str, const char *__restrict delim) +_strtok: + pop bc + pop de + ex (sp), hl + push de + push bc + + ld bc, strtokPtr + push bc ; save_ptr + push hl ; delim + push de ; str + call _strtok_r + pop bc + pop bc + pop bc + ret + +.endif + +;------------------------------------------------------------------------------- + + .section .text + + .global _strtok_r + .type _strtok_r, @function + +; strtok_r(char *__restrict str, const char *__restrict delim, char **__restrict save_ptr) +_strtok_r: + push ix + ld ix, 0 + add ix, sp + ld hl, (ix + 6) ; str + add hl, de + xor a, a + sbc hl, de + jr nz, .L.str_non_null + ; use save_ptr + ld hl, (ix + 12) ; save_ptr + ld hl, (hl) + ld (ix + 6), hl +.L.str_non_null: + cp a, (hl) + jr z, .L.end_of_str + ld de, (ix + 9) ; delim + push de + push hl + call _strspn + ld de, (ix + 6) ; str + add hl, de + xor a, a + cp a, (hl) + jr z, .L.end_of_strspn + + push hl ; ld (ix - 9), hl + + ld de, (ix + 9) ; delim + push de + push hl + call _strcspn + + ld de, (ix - 9) + + add hl, de + xor a, a + cp a, (hl) + jr z, .L.finish + ; nul terminate + ld (hl), a + inc hl +.L.finish: + ld iy, (ix + 12) + ld (iy), hl + ex de, hl + ld sp, ix + pop ix + ret + +.L.end_of_str: +.L.end_of_strspn: + ; return NULL + ld de, 0 + jr .L.finish + + .extern _strspn + .extern _strcspn diff --git a/src/libcxx/include/cstring b/src/libcxx/include/cstring index 7adbf29d1..507f6bc97 100644 --- a/src/libcxx/include/cstring +++ b/src/libcxx/include/cstring @@ -36,6 +36,7 @@ using ::strstr; using ::strrstr; using ::strcasestr; using ::strtok; +using ::strtok_r; using ::strdup; using ::strndup; using ::strcspn; diff --git a/test/standalone/asprintf_fprintf/src/main.c b/test/standalone/asprintf_fprintf/src/main.c index 181d5f8f7..35041b81a 100644 --- a/test/standalone/asprintf_fprintf/src/main.c +++ b/test/standalone/asprintf_fprintf/src/main.c @@ -8,42 +8,107 @@ #include #include -/** -* @brief Tests the following functions/macros: -* boot_sprintf -* boot_snprintf -* boot_asprintf -* asprintf -* fprintf -* stpcpy -* memccpy -*/ +//------------------------------------------------------------------------------ +// Config +//------------------------------------------------------------------------------ + +// define to 0 or 1 +#define DEBUG_DIAGNOSTICS 0 + +// define to 0 or 1 (prevents Clang from replacing functions with builtins) +#define RENAME_FUNCTIONS 1 + +//------------------------------------------------------------------------------ +// Utilities +//------------------------------------------------------------------------------ #define C(expr) if (!(expr)) { return __LINE__; } -#define TEST(test) { ret = test; if (ret != 0) { return ret; }} +#define TEST(test) { ret = test; if (ret != 0) { return ret; } } #define SINK (char*)0xE40000 +#ifndef DEBUG_DIAGNOSTICS +#error "DEBUG_DIAGNOSTICS needs to be defined to 0 or 1" +#endif + +#ifndef RENAME_FUNCTIONS +#error "RENAME_FUNCTIONS needs to be defined to 0 or 1" +#endif + +#if DEBUG_DIAGNOSTICS +#define test_printf printf +#else +#define test_printf(...) +#endif + /* pass NULL into functions without triggering -Wnonnull */ extern void* NULL_ptr; +//------------------------------------------------------------------------------ +// Debug routines +//------------------------------------------------------------------------------ + +__attribute__((__unused__)) static void write_letter(char ch) { + if (isgraph(ch)) { + fputc(ch, stdout); + return; + } + fputc('\\', stdout); + switch (ch) { + case '\0': fputc('0', stdout); return; + case ' ' : fputc('s', stdout); return; + case '\n': fputc('n', stdout); return; + case '\t': fputc('t', stdout); return; + case '\v': fputc('v', stdout); return; + case '\r': fputc('r', stdout); return; + case '\f': fputc('f', stdout); return; + case '\b': fputc('b', stdout); return; + default: { + char buf[sizeof("xFF")]; + boot_sprintf(buf, "x%02X", (unsigned int)ch); + fputs(buf, stdout); + return; + } + } +} + +__attribute__((__unused__)) static void print_mem_string(const char *str, size_t length) { + for (size_t i = 0; i < length; i++) { + write_letter(*str++); + } + fputc('\n', stdout); +} + +__attribute__((__unused__)) static void print_string(const char* str) { + print_mem_string(str, strlen(str)); +} + +//------------------------------------------------------------------------------ +// Functions +//------------------------------------------------------------------------------ + // prevents Clang from replacing function calls with builtins -#if 1 +#if RENAME_FUNCTIONS -void *T_memcpy(void *__restrict dest, const void *__restrict src, size_t n) - __attribute__((nonnull(1, 2))); +void T_bzero(void* s, size_t n); -void *T_memmove(void *dest, const void *src, size_t n) +void *T_memccpy(void *__restrict dest, const void *__restrict src, int c, size_t n) __attribute__((nonnull(1, 2))); -void *T_memset(void *s, int c, size_t n) +void *T_memchr(const void *s, int c, size_t n) __attribute__((nonnull(1))); int T_memcmp(const void *s1, const void *s2, size_t n) __attribute__((nonnull(1, 2))); -void *T_memccpy(void *__restrict dest, const void *__restrict src, int c, size_t n) +void *T_memcpy(void *__restrict dest, const void *__restrict src, size_t n) + __attribute__((nonnull(1, 2))); + +void *T_memmem(const void *haystack, size_t haystack_len, const void *needle, size_t needle_len) + __attribute__((nonnull(1, 3))); + +void *T_memmove(void *dest, const void *src, size_t n) __attribute__((nonnull(1, 2))); void *T_mempcpy(void *__restrict dest, const void *__restrict src, size_t n) @@ -52,61 +117,104 @@ void *T_mempcpy(void *__restrict dest, const void *__restrict src, size_t n) void *T_memrchr(const void *s, int c, size_t n) __attribute__((nonnull(1))); -void *T_memmem(const void *haystack, size_t haystack_len, const void *needle, size_t needle_len) - __attribute__((nonnull(1, 3))); - void *T_memrmem(const void *haystack, size_t haystack_len, const void *needle, size_t needle_len) __attribute__((nonnull(1, 3))); +void *T_memset(void *s, int c, size_t n) + __attribute__((nonnull(1))); + char *T_stpcpy(char *__restrict dest, const char *__restrict src) __attribute__((nonnull(1, 2))); char *T_stpncpy(char *__restrict dest, const char *__restrict src, size_t n) __attribute__((nonnull(1, 2))); +char *T_strcat(char *__restrict dest, const char *__restrict src) + __attribute__((nonnull(1, 2))); + +char *T_strchr(const char *s, int c) + __attribute__((nonnull(1))); + +char *T_strchrnul(const char *s, int c) + __attribute__((nonnull(1))); + +int T_strcmp(const char *s1, const char *s2) + __attribute__((nonnull(1, 2))); + +char *T_strcpy(char *__restrict dest, const char *__restrict src) + __attribute__((nonnull(1, 2))); + size_t T_strlcat(void *__restrict dest, const void *__restrict src, size_t n) __attribute__((nonnull(1, 2))); size_t T_strlen(const char *s) __attribute__((nonnull(1))); -int T_strcmp(const char *s1, const char *s2) +char *T_strncat(char *__restrict dest, const char *__restrict src, size_t n) __attribute__((nonnull(1, 2))); int T_strncmp(const char *s1, const char *s2, size_t n) __attribute__((nonnull(1, 2))); -char *T_strchrnul(const char *s, int c) +char *T_strncpy(char *__restrict dest, const char *__restrict src, size_t n) + __attribute__((nonnull(1, 2))); + +size_t T_strnlen(const char *s, size_t maxlen) + __attribute__((nonnull(1))); + +char *T_strrchr(const char *s, int c) __attribute__((nonnull(1))); char *T_strrstr(const char *haystack, const char *needle) __attribute__((nonnull(1, 2))); -void T_bzero(void* s, size_t n); +char *T_strstr(const char *haystack, const char *needle) + __attribute__((nonnull(1, 2))); + +char *T_strtok(char *__restrict s, const char *__restrict delim) + __attribute__((nonnull(2))); + +char *T_strtok_r(char *__restrict s, const char *__restrict delim, char **__restrict save_ptr) + __attribute__((nonnull(2, 3))); #else +#define T_bzero bzero +#define T_memccpy memccpy +#define T_memchr memchr +#define T_memcmp memcmp #define T_memcpy memcpy +#define T_memmem memmem #define T_memmove memmove -#define T_memset memset -#define T_memcmp memcmp -#define T_memccpy memccpy #define T_mempcpy mempcpy #define T_memrchr memrchr -#define T_memmem memmem #define T_memrmem memrmem +#define T_memset memset #define T_stpcpy stpcpy #define T_stpncpy stpncpy +#define T_strcat strcat +#define T_strchr strchr +#define T_strchrnul strchrnul +#define T_strcmp strcmp +#define T_strcpy strcpy #define T_strlcat strlcat #define T_strlen strlen -#define T_strcmp strcmp +#define T_strncat strncat #define T_strncmp strncmp -#define T_strchrnul strchrnul +#define T_strncpy strncpy +#define T_strnlen strnlen +#define T_strrchr strrchr #define T_strrstr strrstr -#define T_bzero bzero +#define T_strstr strstr +#define T_strtok strtok +#define T_strtok_r strtok_r #endif +//------------------------------------------------------------------------------ +// Globals +//------------------------------------------------------------------------------ + const char gnu_copypasta[] = "I would just like to interject for a moment. What you're referring to "\ "as Linux, is in fact, GNU/Linux, or as I\'ve recently taken to calling "\ @@ -131,6 +239,10 @@ static const int pos_4 = 212; static char* buf = NULL; static FILE* file = NULL; +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + int boot_sprintf_tests(void) { int pos; int len = boot_asprintf( @@ -138,7 +250,7 @@ int boot_sprintf_tests(void) { 123, "asprintf", 076543, 0x9abcd, &pos, 0xFE1, 0 ); if (buf == NULL || len <= 0) { - printf("buf %p len %d\n", buf, len); + test_printf("buf %p len %d\n", buf, len); return __LINE__; } if (buf[len] != '\0') { @@ -146,27 +258,27 @@ int boot_sprintf_tests(void) { } size_t buf_len = T_strlen(buf); if (buf_len != T_strlen(test_1) || buf_len != (size_t)len) { - printf("E: %zu != %zu != %d\n", T_strlen(test_1), buf_len, len); + test_printf("E: %zu != %zu != %d\n", T_strlen(test_1), buf_len, len); return __LINE__; } if (pos != pos_1) { - printf("E: %d != %d\n", pos, pos_1); + test_printf("E: %d != %d\n", pos, pos_1); return __LINE__; } int cmp = strcmp(buf, test_1); if (cmp != 0) { - printf("cmp: %d\n", cmp); + test_printf("cmp: %d\n", cmp); return __LINE__; } char append[128]; int snprintf_test = boot_snprintf(append, 20, "%s", test_1); if (snprintf_test != (int)T_strlen(test_1)) { - printf("sprintf_test: %d\n", snprintf_test); + test_printf("sprintf_test: %d\n", snprintf_test); return __LINE__; } int len_2 = boot_snprintf(append, sizeof(append), "%s", test_1); if (len_2 != (int)T_strlen(test_1)) { - printf("E: %d != %zu\n", len_2, T_strlen(test_1)); + test_printf("E: %d != %zu\n", len_2, T_strlen(test_1)); return __LINE__; } char str2[128]; @@ -178,18 +290,18 @@ int boot_sprintf_tests(void) { return __LINE__; } if (end != &str2[pos_2]) { - printf("diff %p - %p = %td\n", end, str2, (ptrdiff_t)(end - str2)); + test_printf("diff %p - %p = %td\n", end, str2, (ptrdiff_t)(end - str2)); return __LINE__; } int cmp2 = T_strcmp(str2, test_2); if (cmp2 != 0) { - printf("cmp: %d\n", cmp2); + test_printf("cmp: %d\n", cmp2); return __LINE__; } char buf_3[30]; int len_3 = boot_snprintf(buf_3, sizeof(buf_3), test_3); if (len_3 != pos_3) { - printf("E: %d != %d\n", len_3, pos_3); + test_printf("E: %d != %d\n", len_3, pos_3); return __LINE__; } @@ -222,12 +334,12 @@ int boot_sprintf_tests(void) { 5, 10 ); if (len_4 != pos_4) { - printf("E: %d != %d\n", len_4, pos_4); + test_printf("E: %d != %d\n", len_4, pos_4); return __LINE__; } int len_5 = boot_snprintf(SINK, 10, ""); if (len_5 != 0) { - printf("E: %d != 0\n", len_5); + test_printf("E: %d != 0\n", len_5); return __LINE__; } @@ -244,7 +356,7 @@ int nano_tests(void) { 123, "asprintf", 076543, 0x9abcd, &pos, 0xFE1, 0 ); if (buf == NULL || len <= 0) { - printf("buf %p len %d\n", buf, len); + test_printf("buf %p len %d\n", buf, len); return __LINE__; } if (buf[len] != '\0') { @@ -252,27 +364,27 @@ int nano_tests(void) { } size_t buf_len = T_strlen(buf); if (buf_len != T_strlen(test_1) || buf_len != (size_t)len) { - printf("E: %zu != %zu != %d\n", T_strlen(test_1), buf_len, len); + test_printf("E: %zu != %zu != %d\n", T_strlen(test_1), buf_len, len); return __LINE__; } if (pos != pos_1) { - printf("E: %d != %d\n", pos, pos_1); + test_printf("E: %d != %d\n", pos, pos_1); return __LINE__; } int cmp = strcmp(buf, test_1); if (cmp != 0) { - printf("cmp: %d\n", cmp); + test_printf("cmp: %d\n", cmp); return __LINE__; } char append[128]; int snprintf_test = snprintf(append, 20, "%s", test_1); if (snprintf_test != (int)T_strlen(test_1)) { - printf("sprintf_test: %d\n", snprintf_test); + test_printf("sprintf_test: %d\n", snprintf_test); return __LINE__; } int len_2 = snprintf(append, sizeof(append), "%s", test_1); if (len_2 != (int)T_strlen(test_1)) { - printf("E: %d != %zu\n", len_2, T_strlen(test_1)); + test_printf("E: %d != %zu\n", len_2, T_strlen(test_1)); return __LINE__; } char str2[128]; @@ -284,24 +396,24 @@ int nano_tests(void) { return __LINE__; } if (end != &str2[pos_2]) { - printf("diff %p - %p = %td\n", end, str2, (ptrdiff_t)(end - str2)); + test_printf("diff %p - %p = %td\n", end, str2, (ptrdiff_t)(end - str2)); return __LINE__; } int cmp2 = T_strcmp(str2, test_2); if (cmp2 != 0) { - printf("cmp: %d\n", cmp2); + test_printf("cmp: %d\n", cmp2); return __LINE__; } char buf_30[30]; int len_3sn = snprintf(buf_30, sizeof(buf_30), test_3); if (len_3sn != pos_3) { - printf("E: %d != %d\n", len_3sn, pos_3); + test_printf("E: %d != %d\n", len_3sn, pos_3); return __LINE__; } int len_3s = sprintf(buf_30, test_3); if (len_3s != pos_3) { - printf("E: %d != %d\n", len_3s, pos_3); + test_printf("E: %d != %d\n", len_3s, pos_3); return __LINE__; } @@ -334,12 +446,12 @@ int nano_tests(void) { 5, 10 ); if (len_4 != pos_4) { - printf("E: %d != %d\n", len_4, pos_4); + test_printf("E: %d != %d\n", len_4, pos_4); return __LINE__; } int len_5 = snprintf(SINK, 10, ""); if (len_5 != 0) { - printf("E: %d != 0\n", len_5); + test_printf("E: %d != 0\n", len_5); return __LINE__; } @@ -389,7 +501,7 @@ int memccpy_tests(void) { // test zero byte case void* ptr = T_memccpy((void*)0xC0FFEE, (void*)0x123456, 123, 0); if (ptr != NULL) { - printf("%p != NULL\n", ptr); + test_printf("%p != NULL\n", ptr); return __LINE__; } file = fopen(file_name, "wb"); @@ -490,7 +602,7 @@ int mempcpy_test(void) { // test zero byte case void* ptr = T_mempcpy((void*)0xC0FFEE, (void*)0x123456, 0); if (ptr != (void*)0xC0FFEE) { - printf("%p != %p\n", ptr, (void*)0xC0FFEE); + test_printf("%p != %p\n", ptr, (void*)0xC0FFEE); return __LINE__; } char data[192 + 1]; @@ -501,7 +613,7 @@ int mempcpy_test(void) { T_memset(append, 0x34, 64); char* res = T_mempcpy(&data[64], append, 64); if (res != &data[128]) { - printf("%p != %p\n", res, &data[128]); + test_printf("%p != %p\n", res, &data[128]); return __LINE__; } @@ -512,7 +624,7 @@ int mempcpy_test(void) { int cmp = T_memcmp(data, truth, 192); if (cmp != 0) { - printf("cmp: %d\n", cmp); + test_printf("cmp: %d\n", cmp); return __LINE__; } return 0; @@ -550,7 +662,7 @@ int bzero_test(void) { T_bzero(&data[8], 17); int cmp = T_memcmp(data, truth, 32); if (cmp != 0) { - printf("cmp: %d\n", cmp); + test_printf("cmp: %d\n", cmp); return __LINE__; } return 0; @@ -597,6 +709,36 @@ int strncmp_test(void) { return 0; } +int memchr_test(void) { + C(T_memchr(NULL_ptr, 0x00, 0) == NULL_ptr); + C(T_memchr(NULL_ptr, 0xFF, 0) == NULL_ptr); + + C(T_memchr(SINK, 0x00, 0) == NULL); + C(T_memchr(SINK, 0x00, 1) == SINK); + C(T_memchr(SINK, 0xFF, 1) == NULL); + C(T_memchr(SINK, 0x00, 2) == SINK); + C(T_memchr(SINK, 0x00, 500) == SINK); + const char test0[] = "ABCDEFABCDEF\0G"; + char const * const test = &test0[0]; + + const size_t test_size = sizeof(test0) - 2; + const size_t test_strlen = sizeof(test0) - 3; + + C(T_memchr(test, '\0', sizeof(test0)) == &test[12]); + C(T_memchr(test, 'A', test_strlen) == &test[0]); + C(T_memchr(&test[7], 'A', 5) == NULL); + C(T_memchr(&test[6], 'A', 6) == &test[6]); + C(T_memchr(&test[5], 'A', 7) == &test[6]); + C(T_memchr(&test[7], 'B', 5) == &test[7]); + C(T_memchr(&test[8], 'C', 1) == &test[8]); + C(T_memchr(&test[8], 'C', 8) == &test[8]); + C(T_memchr(test, 'G', test_strlen) == NULL); + C(T_memchr(test, 'G', test_size) == NULL); + C(T_memchr(test0, 'G', sizeof(test0)) == &test[test_size]); + + return 0; +} + int memrchr_test(void) { C(T_memrchr(NULL_ptr, 0x00, 0) == NULL_ptr); C(T_memrchr(NULL_ptr, 0xFF, 0) == NULL_ptr); @@ -671,12 +813,13 @@ int strlcat_test(void) { strcpy(dst, src1); C(T_strlcat(dst , src2, 8) == 6); C(strcmp_exact(dst, "FooBar")); strcpy(dst, src1); C(T_strlcat(dst , src2, 9) == 6); C(strcmp_exact(dst, "FooBar")); - strcpy(dst, src1); C(T_strlcat(dst , SINK, 0) == 0); C(strcmp_exact(dst, src1)); - strcpy(dst, src1); C(T_strlcat(dst , SINK, 1) == 1); C(strcmp_exact(dst, src1)); - strcpy(dst, src1); C(T_strlcat(dst , SINK, 2) == 2); C(strcmp_exact(dst, src1)); - strcpy(dst, src1); C(T_strlcat(dst , SINK, 3) == 3); C(strcmp_exact(dst, src1)); - strcpy(dst, src1); C(T_strlcat(dst , SINK, 4) == 3); C(strcmp_exact(dst, src1)); - strcpy(dst, src1); C(T_strlcat(dst , SINK, 5) == 3); C(strcmp_exact(dst, src1)); + C(T_strcpy(dst, src1) == dst); + /* ============== */ C(T_strlcat(dst , SINK, 0) == 0); C(strcmp_exact(dst, src1)); + T_strcpy(dst, src1); C(T_strlcat(dst , SINK, 1) == 1); C(strcmp_exact(dst, src1)); + T_strcpy(dst, src1); C(T_strlcat(dst , SINK, 2) == 2); C(strcmp_exact(dst, src1)); + T_strcpy(dst, src1); C(T_strlcat(dst , SINK, 3) == 3); C(strcmp_exact(dst, src1)); + T_strcpy(dst, src1); C(T_strlcat(dst , SINK, 4) == 3); C(strcmp_exact(dst, src1)); + T_strcpy(dst, src1); C(T_strlcat(dst , SINK, 5) == 3); C(strcmp_exact(dst, src1)); C(T_strlcat(NULL_ptr, SINK, 0) == 0); C(T_strlcat(NULL_ptr, src1, 0) == 3); @@ -695,6 +838,49 @@ int strlcat_test(void) { return 0; } +int strncpy_test(void) { + char text[6]; + + C(T_strncpy(NULL_ptr, "", 0) == NULL_ptr + 0); + C(T_strncpy(NULL_ptr, "foobar", 0) == NULL_ptr + 0); + + memset(text, '\xee', 6); + + C(T_strncpy(text, "1", 5) == text); + C(memcmp(text, "1\0\0\0\0\xee", 6) == 0); + + C(T_strncpy(text, "1234", 5) == text); + C(memcmp(text, "1234\0\xee", 6) == 0); + + C(T_strncpy(text, "12345", 5) == text); + C(memcmp(text, "12345\xee", 6) == 0); + + C(T_strncpy(text, "123456", 5) == text); + C(memcmp(text, "12345\xee", 6) == 0); + + memset(text, '\xff', 6); + + C(T_strncpy(text, "", 0) == text); + C(memcmp(text, "\xff\xff\xff\xff\xff\xff", 6) == 0); + + C(T_strncpy(text, "123456", 1) == text); + C(memcmp(text, "1\xff\xff\xff\xff\xff", 6) == 0); + + C(T_strncpy(text, "6", 1) == text); + C(memcmp(text, "6\xff\xff\xff\xff\xff", 6) == 0); + + C(T_strncpy(text, "", 1) == text); + C(memcmp(text, "\0\xff\xff\xff\xff\xff", 6) == 0); + + C(T_strncpy(text, "a", 2) == text); + C(memcmp(text, "a\0\xff\xff\xff\xff", 6) == 0); + + C(T_strncpy(text, "", 5) == text); + C(memcmp(text, "\0\0\0\0\0\xff", 6) == 0); + + return 0; +} + int stpncpy_test(void) { char text[6]; @@ -721,19 +907,19 @@ int stpncpy_test(void) { C(memcmp(text, "\xff\xff\xff\xff\xff\xff", 6) == 0); C(T_stpncpy(text, "123456", 1) == text + 1); - C(memcmp(text, "1\xff\xff\xff\xff\xff", 1) == 0); + C(memcmp(text, "1\xff\xff\xff\xff\xff", 6) == 0); C(T_stpncpy(text, "6", 1) == text + 1); - C(memcmp(text, "6\xff\xff\xff\xff\xff", 1) == 0); + C(memcmp(text, "6\xff\xff\xff\xff\xff", 6) == 0); C(T_stpncpy(text, "", 1) == text + 0); - C(memcmp(text, "\0\xff\xff\xff\xff\xff", 1) == 0); + C(memcmp(text, "\0\xff\xff\xff\xff\xff", 6) == 0); C(T_stpncpy(text, "a", 2) == text + 1); - C(memcmp(text, "a\0\xff\xff\xff\xff", 1) == 0); + C(memcmp(text, "a\0\xff\xff\xff\xff", 6) == 0); C(T_stpncpy(text, "", 5) == text + 0); - C(memcmp(text, "\0\0\0\0\0\xff", 1) == 0); + C(memcmp(text, "\0\0\0\0\0\xff", 6) == 0); return 0; } @@ -952,6 +1138,38 @@ int memrmem_test(void) { return 0; } +int strstr_test(void) { + const char str1[] = "abcdef123\0aababc123"; + + C(T_strstr(SINK, SINK) == SINK); + C(T_strstr(SINK, str1) == NULL); + C(T_strstr(str1, SINK) == str1); + C(T_strstr(str1, str1) == str1); + + C(T_strstr(str1 + 0, str1 + 6) == str1 + 6); + C(T_strstr(str1 + 9, str1 + 9) == str1 + 9); + C(T_strstr(str1 + 10, "ab") == str1 + 11); + C(T_strstr(str1 + 10, "aa") == str1 + 10); + C(T_strstr(str1 + 10, "b" ) == str1 + 12); + C(T_strstr(str1 + 10, "ba") == str1 + 12); + C(T_strstr(str1 + 10, "aac") == NULL); + + C(T_strstr(gnu_copypasta, "" ) == gnu_copypasta + 0); + C(T_strstr(gnu_copypasta, "a" ) == gnu_copypasta + 35); + C(T_strstr(gnu_copypasta, "by" ) == gnu_copypasta + 286); + C(T_strstr(gnu_copypasta, "GNU" ) == gnu_copypasta + 92); + C(T_strstr(gnu_copypasta, "full" ) == gnu_copypasta + 245); + C(T_strstr(gnu_copypasta, "linux" ) == NULL); + C(T_strstr(gnu_copypasta, "Linux" ) == gnu_copypasta + 73); + C(T_strstr(gnu_copypasta, "I would" ) == gnu_copypasta + 0); + C(T_strstr(gnu_copypasta, "POSIX" ) == gnu_copypasta + 386); + C(T_strstr(gnu_copypasta, "POsIX" ) == NULL); + C(T_strstr(gnu_copypasta, "system" ) == gnu_copypasta + 186); + C(T_strstr(gnu_copypasta, " component") == gnu_copypasta + 229); + + return 0; +} + int strrstr_test(void) { const char str1[] = "abcdef123\0aababc123"; @@ -968,6 +1186,7 @@ int strrstr_test(void) { C(T_strrstr(str1 + 10, "ba") == str1 + 12); C(T_strrstr(str1 + 10, "aac") == NULL); + C(T_strrstr(gnu_copypasta, "" ) == gnu_copypasta + 390); C(T_strrstr(gnu_copypasta, "a" ) == gnu_copypasta + 372); C(T_strrstr(gnu_copypasta, "by" ) == gnu_copypasta + 383); C(T_strrstr(gnu_copypasta, "GNU" ) == gnu_copypasta + 293); @@ -994,6 +1213,155 @@ int strchrnul_test(void) { return 0; } +int strtok_test(void) { + const char input_empty[] = "\x00\xff"; + const char truth_empty[] = "\x00\xff"; + const char input_text[] = "?a???b,,,#c"; + const char truth_text[] = "?a\0??b\0,,#c"; + const char input_str[] = "A bird came... down the walk"; + const char truth_str[] = "A\0bird\0came\0.. down\0the\0walk"; + const char delim[] = " .,"; + char empty[sizeof(input_empty)]; + char text[sizeof(input_text)]; + char str[sizeof(input_str)]; + + /* strtok */ { + char *token; + memcpy(empty, input_empty, sizeof(input_empty)); + strcpy(text, input_text); + strcpy(str, input_str); + + token = T_strtok(empty, ""); + C(token == NULL); + + token = T_strtok(NULL, ""); + C(token == NULL); + + C(T_memcmp(empty, truth_empty, sizeof(truth_empty)) == 0); + + // token points to the token "a" + token = T_strtok(text, "?"); + C(token == text + 1); + C(strcmp_exact(token, "a")); + + // token points to the token "??b" + token = T_strtok(NULL, ","); + C(token == text + 3); + C(strcmp_exact(token, "??b")); + + // token points to the token "c" + token = T_strtok(NULL, "#,"); + C(token == text + 10); + C(strcmp_exact(token, "c")); + + // token is a null pointer + token = T_strtok(NULL, "?"); + C(token == NULL); + + C(T_memcmp(text, truth_text, sizeof(truth_text)) == 0); + + strcpy(str, input_str); + + token = strtok(str , delim); + C(token == str + 0); + + token = strtok(NULL, delim); + C(token == str + 2); + + token = strtok(NULL, delim); + C(token == str + 7); + + token = strtok(NULL, delim); + C(token == str + 15); + + token = strtok(NULL, delim); + C(token == str + 20); + + token = strtok(NULL, delim); + C(token == str + 24); + + token = strtok(NULL, delim); + C(token == NULL); + + C(T_memcmp(str, truth_str, sizeof(truth_str)) == 0); + } + + /* strtok_r */ { + char *save_ptr; + char *token; + memcpy(empty, input_empty, sizeof(input_empty)); + strcpy(text, input_text); + strcpy(str, input_str); + + token = T_strtok_r(empty, "", &save_ptr); + C(token == NULL); + C(save_ptr == empty); + + token = T_strtok_r(NULL, "", &save_ptr); + C(token == NULL); + C(save_ptr == empty); + + C(T_memcmp(empty, truth_empty, sizeof(truth_empty)) == 0); + + // token points to the token "a" + token = T_strtok_r(text, "?", &save_ptr); + C(token == text + 1); + C(strcmp_exact(token, "a")); + C(save_ptr == text + 3); + + // token points to the token "??b" + token = T_strtok_r(NULL, ",", &save_ptr); + C(token == text + 3); + C(strcmp_exact(token, "??b")); + C(save_ptr == text + 7); + + // token points to the token "c" + token = T_strtok_r(NULL, "#,", &save_ptr); + C(token == text + 10); + C(strcmp_exact(token, "c")); + C(save_ptr == text + 11); + + // token is a null pointer + token = T_strtok_r(NULL, "?", &save_ptr); + C(token == NULL); + C(save_ptr == text + 11); + + C(T_memcmp(text, truth_text, sizeof(truth_text)) == 0); + + token = T_strtok_r(str , delim, &save_ptr); + C(token == str + 0); + C(save_ptr == str + 2); + + token = T_strtok_r(NULL, delim, &save_ptr); + C(token == str + 2); + C(save_ptr == str + 7); + + token = T_strtok_r(NULL, delim, &save_ptr); + C(token == str + 7); + C(save_ptr == str + 12); + + token = T_strtok_r(NULL, delim, &save_ptr); + C(token == str + 15); + C(save_ptr == str + 20); + + token = T_strtok_r(NULL, delim, &save_ptr); + C(token == str + 20); + C(save_ptr == str + 24); + + token = T_strtok_r(NULL, delim, &save_ptr); + C(token == str + 24); + C(save_ptr == str + 28); + + token = T_strtok_r(NULL, delim, &save_ptr); + C(token == NULL); + C(save_ptr == str + 28); + + C(T_memcmp(str, truth_str, sizeof(truth_str)) == 0); + } + + return 0; +} + int run_tests(void) { int ret = 0; /* boot_asprintf */ @@ -1018,39 +1386,22 @@ int run_tests(void) { TEST(mempcpy_test()); TEST(bzero_test()); TEST(strncmp_test()); + TEST(memchr_test()); TEST(memrchr_test()); TEST(memmove_test()); TEST(strlcat_test()); + TEST(strncpy_test()); TEST(stpncpy_test()); TEST(memmem_test()); TEST(memrmem_test()); + TEST(strstr_test()); TEST(strrstr_test()); TEST(strchrnul_test()); + TEST(strtok_test()); return 0; } -#if 0 -static void write_letter(char c) { - if (isgraph(c)) { - fputc(c, stdout); - return; - } - fputc('\\', stdout); - switch (c) { - case '\0': fputc('0', stdout); return; - case ' ': fputc('s', stdout); return; - case '\n': fputc('n', stdout); return; - case '\t': fputc('t', stdout); return; - case '\v': fputc('v', stdout); return; - case '\r': fputc('r', stdout); return; - case '\f': fputc('f', stdout); return; - case '\b': fputc('b', stdout); return; - default: printf("x%02X", (unsigned int)c); return; - } -} -#endif - int main(void) { os_ClrHome(); diff --git a/test/standalone/asprintf_fprintf/src/rename.s b/test/standalone/asprintf_fprintf/src/rename.s index aa23678af..a2cb56b5a 100644 --- a/test/standalone/asprintf_fprintf/src/rename.s +++ b/test/standalone/asprintf_fprintf/src/rename.s @@ -1,56 +1,162 @@ .assume adl = 1 + .section .rodata._NULL_ptr + + .global _NULL_ptr +_NULL_ptr: + db $00, $00, $00 + +;------------------------------------------------------------------------------- + .section .text - .global _T_memset, _T_memcpy, _T_memmove, _T_memcmp, _T_memccpy, _T_mempcpy, _T_memrchr, _T_memmem, _T_memrmem - .global _T_strlen, _T_strcmp, _T_strncmp, _T_stpcpy, _T_stpncpy, _T_strlcat, _T_strchrnul, _T_strrstr .global _T_bzero + .global _T_memccpy + .global _T_memchr + .global _T_memcmp + .global _T_memcpy + .global _T_memmem + .global _T_memmove + .global _T_mempcpy + .global _T_memrchr + .global _T_memrmem + .global _T_memset + .global _T_stpcpy + .global _T_stpncpy + .global _T_strcat + .global _T_strchr + .global _T_strchrnul + .global _T_strcmp + .global _T_strcpy + .global _T_strlcat + .global _T_strlen + .global _T_strncat + .global _T_strncmp + .global _T_strncpy + .global _T_strnlen + .global _T_strrchr + .global _T_strrstr + .global _T_strstr + .global _T_strtok + .global _T_strtok_r + +;------------------------------------------------------------------------------- + +_T_bzero: + jp _bzero + +_T_memccpy: + jp _memccpy + +_T_memchr: + jp _memchr + +_T_memcmp: + jp _memcmp -_T_memset: - jp _memset _T_memcpy: jp _memcpy + +_T_memmem: + jp _memmem + _T_memmove: jp _memmove -_T_memcmp: - jp _memcmp -_T_memccpy: - jp _memccpy + _T_mempcpy: jp _mempcpy + _T_memrchr: jp _memrchr -_T_memmem: - jp _memmem + _T_memrmem: jp _memrmem -_T_strlen: - jp _strlen -_T_strcmp: - jp _strcmp -_T_strncmp: - jp _strncmp +_T_memset: + jp _memset + _T_stpcpy: jp _stpcpy + _T_stpncpy: jp _stpncpy -_T_strlcat: - jp _strlcat + +_T_strcat: + jp _strcat + +_T_strchr: + jp _strchr + _T_strchrnul: jp _strchrnul + +_T_strcmp: + jp _strcmp + +_T_strcpy: + jp _strcpy + +_T_strlcat: + jp _strlcat + +_T_strlen: + jp _strlen + +_T_strncat: + jp _strncat + +_T_strncmp: + jp _strncmp + +_T_strncpy: + jp _strncpy + +_T_strnlen: + jp _strnlen + +_T_strrchr: + jp _strrchr + _T_strrstr: jp _strrstr -_T_bzero: - jp _bzero +_T_strstr: + jp _strstr - .section .rodata._NULL_ptr +_T_strtok: + jp _strtok - .global _NULL_ptr -_NULL_ptr: - db $00, $00, $00 +_T_strtok_r: + jp _strtok_r + +;------------------------------------------------------------------------------- - .extern _memset, _memcpy, _memmove, _memcmp, _memccpy, _mempcpy, _memrchr, _memmem, _memrmem - .extern _strlen, _strcmp, _strncmp, _stpcpy, _stpncpy, _strlcat, _strchrnul, _strrstr - .extern _bzero + .global _bzero + .global _memccpy + .global _memchr + .global _memcmp + .global _memcpy + .global _memmem + .global _memmove + .global _mempcpy + .global _memrchr + .global _memrmem + .global _memset + .global _stpcpy + .global _stpncpy + .global _strcat + .global _strchr + .global _strchrnul + .global _strcmp + .global _strcpy + .global _strlcat + .global _strlen + .global _strncat + .global _strncmp + .global _strncpy + .global _strnlen + .global _strrchr + .global _strrstr + .global _strstr + .global _strtok + .global _strtok_r