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