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