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