diff options
Diffstat (limited to 'libc/arch-mips/string/mips_strlen.c')
-rw-r--r-- | libc/arch-mips/string/mips_strlen.c | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/libc/arch-mips/string/mips_strlen.c b/libc/arch-mips/string/mips_strlen.c new file mode 100644 index 0000000..9fb7e6a --- /dev/null +++ b/libc/arch-mips/string/mips_strlen.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2010 MIPS Technologies, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with + * the distribution. + * * Neither the name of MIPS Technologies Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "mips-string-ops.h" + +#define do_strlen_word(__av) {\ + if (detect_zero(x,x,_01s,_80s)) break;\ + x = __av;\ + cnt += sizeof (unsigned);\ + } + +#define do_strlen_byte(__x) {\ + if ((bx.b.B##__x) == 0) break;\ + ++cnt;\ + } + +#if SMOKE_TEST_MIPS_STRLEN +#define strlen my_strlen +#endif + +int +strlen (const void *_a) +{ + int cnt = 0; + unsigned x; + + /* align the string to word boundary so we can do word at a time. */ + if ((cvt_ptr_to (unsigned, _a) & (sizeof (unsigned) - 1)) != 0) + { + if ((cvt_ptr_to (unsigned, _a) & 1) != 0) + { + if (get_byte (_a, 0) == 0) + return cnt; + /* set bit 1 so 2-bytes are checked and incremented. */ + inc_ptr_as (char *, _a, 1); + ++cnt; + } + if ((cvt_ptr_to (unsigned, _a) & 2) != 0) + { + if (get_byte (_a, 0) == 0) + return cnt + 0; + if (get_byte (_a, 1) == 0) + return cnt + 1; + inc_ptr_as (char *, _a, 2); + cnt += 2; + } + } + +#if __mips64 +#error strlen: mips64 check for 4-byte alignment not implemented. +#endif + + if (1) + { + def_and_set_01 (_01s); + def_and_set_80 (_80s); + + /* as advantagous as it is to performance, this code cannot pre-load + the following word, nor can it prefetch the next line at the start + of the loop since the string can be at the end of a page with the + following page unmapped. There are tests in the suite to catch + any attempt to go beyond the current word. */ + x = get_word (_a, 0); + while (1) + { + /* doing 8 words should cover most strings. */ + do_strlen_word (get_word (_a, 1)); + do_strlen_word (get_word (_a, 2)); + do_strlen_word (get_word (_a, 3)); + do_strlen_word (get_word (_a, 4)); + do_strlen_word (get_word (_a, 5)); + do_strlen_word (get_word (_a, 6)); + do_strlen_word (get_word (_a, 7)); + do_strlen_word (get_word (_a, 8)); + inc_ptr_as (unsigned *, _a, 8); + } + } + while (1) + { + /* pull apart the last word processed and find the zero. */ + bitfields_t bx; + bx.v = x; +#if __mips64 + do_strlen_byte (0); + do_strlen_byte (1); + do_strlen_byte (2); + do_strlen_byte (3); + do_strlen_byte (4); + do_strlen_byte (5); + do_strlen_byte (6); +#else + do_strlen_byte (0); + do_strlen_byte (1); + do_strlen_byte (2); +#endif + /* last byte is zero */ + break; + } + return cnt; +} + +#undef do_strlen_byte +#undef do_strlen_word + +#if SMOKE_TEST_MIPS_STRLEN +#include <stdio.h> +char str1[] = "DHRYSTONE PROGRAM, 1'ST STRING"; +char str2[] = "DHRYSTONE PROGRAM, 2'ST STRING"; + +char str3[] = "another string"; +char str4[] = "another"; + +char str5[] = "somes tring"; +char str6[] = "somes_tring"; + +char str7[16], str8[16]; + +static char * +chk (unsigned mine, unsigned libs, int *errors) +{ + static char answer[1024]; + char *result = mine == libs ? "PASS" : "FAIL"; + sprintf (answer, "new_strlen=%d: lib_strlen=%d: %s!", mine, libs, result); + if (mine != libs) + (*errors)++; + return answer; +} + +int +main (int argc, char **argv) +{ + int errors = 0; + /* set -1 in one position */ + str6[5] = 0xff; + /* set zero in same position with junk in following 3 */ + str7[0] = str8[0] = 0; + str7[1] = 0xff; + str7[2] = 'a'; + str7[3] = 2; + str8[1] = 's'; + str8[2] = -2; + str8[3] = 0; + + fprintf (stderr, "========== mips_strlen%s test...\n", + argv[0] ? argv[0] : "unknown strlen"); +#define P(__x,__y) {\ + int a = my_strlen(__x + __y);\ + int b = (strlen)(__x + __y) /* library version */;\ + fprintf(stderr,"%s+%d: %s\n",#__x,__y,chk(a,b,&errors));\ + } + + P (str1, 0); + P (str1, 1); + P (str1, 2); + P (str1, 3); + + P (str2, 0); + P (str2, 1); + P (str2, 2); + P (str2, 3); + + P (str3, 0); + P (str3, 1); + P (str3, 2); + P (str3, 3); + + P (str4, 0); + P (str4, 1); + P (str4, 2); + P (str4, 3); + + P (str5, 0); + P (str5, 1); + P (str5, 2); + P (str5, 3); + + P (str6, 0); + P (str6, 1); + P (str6, 2); + P (str6, 3); + + P (str7, 0); + P (str7, 1); + P (str7, 2); + P (str7, 3); + + P (str8, 0); + P (str8, 1); + P (str8, 2); + P (str8, 3); + + return errors; +} +#endif |