1/*
2 * Copyright (c) 2010 MIPS Technologies, Inc.
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 *      * Redistributions of source code must retain the above copyright
11 *        notice, this list of conditions and the following disclaimer.
12 *      * Redistributions in binary form must reproduce the above copyright
13 *        notice, this list of conditions and the following disclaimer
14 *        in the documentation and/or other materials provided with
15 *        the distribution.
16 *      * Neither the name of MIPS Technologies Inc. nor the names of its
17 *        contributors may be used to endorse or promote products derived
18 *        from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <string.h>
34#include "mips-string-ops.h"
35
36#define do_strlen_word(__av) {\
37    if (detect_zero(x,x,_01s,_80s)) break;\
38    x = __av;\
39    cnt += sizeof (unsigned);\
40    }
41
42#define do_strlen_byte(__x) {\
43  if ((bx.b.B##__x) == 0) break;\
44  ++cnt;\
45  }
46
47#if SMOKE_TEST_MIPS_STRLEN
48#define strlen my_strlen
49#endif
50
51size_t
52strlen (const char *_a)
53{
54  int cnt = 0;
55  unsigned long x;
56
57  /* align the string to word boundary so we can do word at a time.  */
58  if ((cvt_ptr_to (unsigned long, _a) & (sizeof (unsigned long) - 1)) != 0)
59    {
60      if ((cvt_ptr_to (unsigned long, _a) & 1) != 0)
61	{
62	  if (get_byte (_a, 0) == 0)
63	    return cnt;
64	  /* set bit 1 so 2-bytes are checked and incremented. */
65	  inc_ptr_as (char *, _a, 1);
66	  ++cnt;
67	}
68      if ((cvt_ptr_to (unsigned long, _a) & 2) != 0)
69	{
70	  if (get_byte (_a, 0) == 0)
71	    return cnt + 0;
72	  if (get_byte (_a, 1) == 0)
73	    return cnt + 1;
74	  inc_ptr_as (char *, _a, 2);
75	  cnt += 2;
76	}
77    }
78
79#if __mips64
80#error strlen: mips64 check for 4-byte alignment not implemented.
81#endif
82
83  if (1)
84    {
85      def_and_set_01 (_01s);
86      def_and_set_80 (_80s);
87
88      /* as advantagous as it is to performance, this code cannot pre-load
89         the following word, nor can it prefetch the next line at the start
90         of the loop since the string can be at the end of a page with the
91         following page unmapped. There are tests in the suite to catch
92         any attempt to go beyond the current word. */
93      x = get_word (_a, 0);
94      while (1)
95	{
96	  /* doing 8 words should cover most strings.  */
97	  do_strlen_word (get_word (_a, 1));
98	  do_strlen_word (get_word (_a, 2));
99	  do_strlen_word (get_word (_a, 3));
100	  do_strlen_word (get_word (_a, 4));
101	  do_strlen_word (get_word (_a, 5));
102	  do_strlen_word (get_word (_a, 6));
103	  do_strlen_word (get_word (_a, 7));
104	  do_strlen_word (get_word (_a, 8));
105	  inc_ptr_as (unsigned long*, _a, 8);
106	}
107    }
108  while (1)
109    {
110      /* pull apart the last word processed and find the zero.  */
111      bitfields_t bx;
112      bx.v = x;
113#if __mips64
114      do_strlen_byte (0);
115      do_strlen_byte (1);
116      do_strlen_byte (2);
117      do_strlen_byte (3);
118      do_strlen_byte (4);
119      do_strlen_byte (5);
120      do_strlen_byte (6);
121#else
122      do_strlen_byte (0);
123      do_strlen_byte (1);
124      do_strlen_byte (2);
125#endif
126      /* last byte is zero */
127      break;
128    }
129  return cnt;
130}
131
132#undef do_strlen_byte
133#undef do_strlen_word
134
135#if SMOKE_TEST_MIPS_STRLEN
136#include <stdio.h>
137char str1[] = "DHRYSTONE PROGRAM, 1'ST STRING";
138char str2[] = "DHRYSTONE PROGRAM, 2'ST STRING";
139
140char str3[] = "another string";
141char str4[] = "another";
142
143char str5[] = "somes tring";
144char str6[] = "somes_tring";
145
146char str7[16], str8[16];
147
148static char *
149chk (unsigned long mine, unsigned long libs, int *errors)
150{
151  static char answer[1024];
152  char *result = mine == libs ? "PASS" : "FAIL";
153  sprintf (answer, "new_strlen=%d: lib_strlen=%d: %s!", mine, libs, result);
154  if (mine != libs)
155    (*errors)++;
156  return answer;
157}
158
159int
160main (int argc, char **argv)
161{
162  int errors = 0;
163  /* set -1 in one position */
164  str6[5] = 0xff;
165  /* set zero in same position with junk in following 3 */
166  str7[0] = str8[0] = 0;
167  str7[1] = 0xff;
168  str7[2] = 'a';
169  str7[3] = 2;
170  str8[1] = 's';
171  str8[2] = -2;
172  str8[3] = 0;
173
174  fprintf (stderr, "========== mips_strlen%s test...\n",
175	   argv[0] ? argv[0] : "unknown strlen");
176#define P(__x,__y) {\
177    int a = my_strlen(__x + __y);\
178    int b = (strlen)(__x + __y) /* library version */;\
179    fprintf(stderr,"%s+%d: %s\n",#__x,__y,chk(a,b,&errors));\
180    }
181
182  P (str1, 0);
183  P (str1, 1);
184  P (str1, 2);
185  P (str1, 3);
186
187  P (str2, 0);
188  P (str2, 1);
189  P (str2, 2);
190  P (str2, 3);
191
192  P (str3, 0);
193  P (str3, 1);
194  P (str3, 2);
195  P (str3, 3);
196
197  P (str4, 0);
198  P (str4, 1);
199  P (str4, 2);
200  P (str4, 3);
201
202  P (str5, 0);
203  P (str5, 1);
204  P (str5, 2);
205  P (str5, 3);
206
207  P (str6, 0);
208  P (str6, 1);
209  P (str6, 2);
210  P (str6, 3);
211
212  P (str7, 0);
213  P (str7, 1);
214  P (str7, 2);
215  P (str7, 3);
216
217  P (str8, 0);
218  P (str8, 1);
219  P (str8, 2);
220  P (str8, 3);
221
222  return errors;
223}
224#endif
225