1 /*
2  * strncpy.S -- strncmp function.  On at least some MIPS chips, you get better
3  * code by hand unrolling the loops, and by using store words to zero the
4  * remainder of the buffer than the default newlib C version.
5  *
6  * Copyright (c) 2001 Red Hat, Inc.
7  *
8  * The authors hereby grant permission to use, copy, modify, distribute,
9  * and license this software and its documentation for any purpose, provided
10  * that existing copyright notices are retained in all copies and that this
11  * notice is included verbatim in any distributions. No written agreement,
12  * license, or royalty fee is required for any of the authorized uses.
13  * Modifications to this software may be copyrighted by their authors
14  * and need not follow the licensing terms described here, provided that
15  * the new terms are clearly indicated on the first page of each file where
16  * they apply.  */
17 
18 #include <string.h>
19 #include <stddef.h>
20 #include <stdlib.h>
21 #include <stdint.h>
22 
23 #if !defined(__GNUC__) || (__GNUC__ < 3)
24 #define __builtin_expect(a,b) a
25 
26 #else
27 #ifdef __mips64
28 /* Don't use limits test for the size of long, in order to allow the use of
29    64-bit stores on MIPS3 machines, even if -mlong32 was used.  */
30 typedef unsigned word_type __attribute__ ((mode (DI)));
31 #else
32 typedef unsigned word_type __attribute__ ((mode (SI)));
33 #endif
34 
35 typedef unsigned si_type __attribute__ ((mode (SI)));
36 typedef unsigned hi_type __attribute__ ((mode (HI)));
37 
38 #ifndef UNROLL_FACTOR
39 #define UNROLL_FACTOR 4
40 
41 #elif (UNROLL_FACTOR != 2) && (UNROLL_FACTOR != 4)
42 #error "UNROLL_FACTOR must be 2 or 4"
43 #endif
44 #endif
45 
46 char *
strncpy(char * dst0,const char * src0,size_t count)47 strncpy (char *dst0, const char *src0, size_t count)
48 {
49 #if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) || defined(__mips16) || !defined(__GNUC__) || (__GNUC__ < 3)
50   char *dst, *end;
51   const char *src;
52   int ch;
53 
54   dst = dst0;
55   src = src0;
56   end = dst + count;
57   while (dst != end)
58     {
59       *dst++ = ch = *src++;
60       if (__builtin_expect (ch == '\0', 0))
61 	{
62 	  while (dst != end)
63 	    *dst++ = '\0';
64 
65 	  break;
66 	}
67     }
68 
69   return dst0;
70 
71 #else
72   unsigned char *dst;
73   unsigned char *end;
74   const unsigned char *src;
75   int ch0, ch1;
76 #if UNROLL_FACTOR > 2
77   int ch2, ch3;
78 #endif
79   int ch;
80   int odd_bytes;
81 
82   dst = (unsigned char *)dst0;
83   src = (unsigned const char *)src0;
84   /* Take care of any odd bytes in the source data because we
85    * want to unroll where we read ahead 2 or 4 bytes at a time and then
86    * check each byte for the null terminator.  This can result in
87    * a segfault for the case where the source pointer is unaligned,
88    * the null terminator is in valid memory, but reading 2 or 4 bytes at a
89    * time blindly eventually goes outside of valid memory. */
90   while (((uintptr_t) src & (UNROLL_FACTOR - 1)) != 0 && count > 0)
91     {
92       *dst++ = ch = *src++;
93       --count;
94       if (ch == '\0')
95 	{
96           end = dst + count;
97 	  while (dst != end)
98 	    *dst++ = '\0';
99 
100 	  return dst0;
101 	}
102     }
103 
104   if (__builtin_expect (count >= 4, 1))
105     {
106       odd_bytes = (count & (UNROLL_FACTOR - 1));
107       count -= odd_bytes;
108 
109       do
110 	{
111 	  ch0 = src[0];
112 	  ch1 = src[1];
113 #if UNROLL_FACTOR > 2
114 	  ch2 = src[2];
115 	  ch3 = src[3];
116 #endif
117 	  src += UNROLL_FACTOR;
118 	  count -= UNROLL_FACTOR;
119 
120 	  dst[0] = ch0;
121 	  if (ch0 == '\0')
122 	    goto found_null0;
123 
124 	  dst[1] = ch1;
125 	  if (ch1 == '\0')
126 	    goto found_null1;
127 
128 #if UNROLL_FACTOR > 2
129 	  dst[2] = ch2;
130 	  if (ch2 == '\0')
131 	    goto found_null2;
132 
133 	  dst[3] = ch3;
134 	  if (ch3 == '\0')
135 	    goto found_null3;
136 #endif
137 
138 	  dst += UNROLL_FACTOR;
139 	}
140       while (count);
141 
142       /* fall through, count == 0, no null found, deal with last bytes */
143       count = odd_bytes;
144     }
145 
146   end = dst + count;
147   while (dst != end)
148     {
149       *dst++ = ch = *src++;
150       if (ch == '\0')
151 	{
152 	  while (dst != end)
153 	    *dst++ = '\0';
154 
155 	  break;
156 	}
157     }
158 
159   return dst0;
160 
161   /* Found null byte in first byte, count has been decremented by 4, null has
162      been stored in dst[0].  */
163  found_null0:
164   count++;			/* add 1 to cover remaining byte */
165   dst -= 1;			/* adjust dst += 4 gets correct ptr */
166 
167   /* Found null byte in second byte, count has been decremented by 4, null has
168      been stored in dst[1].  */
169  found_null1:
170 #if UNROLL_FACTOR > 2
171   count++;			/* add 1 to cover remaining byte */
172   dst -= 1;			/* adjust dst += 4 gets correct ptr */
173 
174   /* Found null byte in third byte, count has been decremented by 4, null has
175      been stored in dst[2].  */
176  found_null2:
177   count++;			/* add 1 to cover remaining byte */
178   dst -= 1;			/* adjust dst += 4 gets correct ptr */
179 
180   /* Found null byte in fourth byte, count is accurate, dst has not been
181      updated yet.  */
182  found_null3:
183 #endif
184   count += odd_bytes;		/* restore odd byte count */
185   dst += UNROLL_FACTOR;
186 
187   /* Zero fill remainder of the array.  Unroll the loop, and use word/dword
188      stores where we can.  */
189   while (count && (((long)dst) & (sizeof (word_type) - 1)) != 0)
190     {
191       count--;
192       *dst++ = 0;
193     }
194 
195   while (count >= UNROLL_FACTOR*sizeof (word_type))
196     {
197       count -= UNROLL_FACTOR*sizeof (word_type);
198       dst += UNROLL_FACTOR*sizeof (word_type);
199 #if UNROLL_FACTOR > 2
200       ((word_type *)(void *)dst)[-4] = 0;
201       ((word_type *)(void *)dst)[-3] = 0;
202 #endif
203       ((word_type *)(void *)dst)[-2] = 0;
204       ((word_type *)(void *)dst)[-1] = 0;
205     }
206 
207 #if UNROLL_FACTOR > 2
208   if (count >= 2*sizeof (word_type))
209     {
210       count -= 2*sizeof (word_type);
211       ((word_type *)(void *)dst)[0] = 0;
212       ((word_type *)(void *)dst)[1] = 0;
213       dst += 2*sizeof (word_type);
214     }
215 #endif
216 
217   if (count >= sizeof (word_type))
218     {
219       count -= sizeof (word_type);
220       ((word_type *)(void *)dst)[0] = 0;
221       dst += sizeof (word_type);
222     }
223 
224 #ifdef __mips64
225   if (count >= sizeof (si_type))
226     {
227       count -= sizeof (si_type);
228       ((si_type *)(void *)dst)[0] = 0;
229       dst += sizeof (si_type);
230     }
231 #endif
232 
233   if (count >= sizeof (hi_type))
234     {
235       count -= sizeof (hi_type);
236       ((hi_type *)(void *)dst)[0] = 0;
237       dst += sizeof (hi_type);
238     }
239 
240   if (count)
241     *dst = '\0';
242 
243   return dst0;
244 #endif
245 }
246