]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 | 2 | /* |
1da177e4 LT |
3 | * Optimized string functions |
4 | * | |
5 | * S390 version | |
a53c8fab | 6 | * Copyright IBM Corp. 2004 |
1da177e4 LT |
7 | * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) |
8 | */ | |
9 | ||
10 | #define IN_ARCH_STRING_C 1 | |
11 | ||
12 | #include <linux/types.h> | |
d3217967 PG |
13 | #include <linux/string.h> |
14 | #include <linux/export.h> | |
1da177e4 LT |
15 | |
16 | /* | |
17 | * Helper functions to find the end of a string | |
18 | */ | |
19 | static inline char *__strend(const char *s) | |
20 | { | |
21 | register unsigned long r0 asm("0") = 0; | |
22 | ||
23 | asm volatile ("0: srst %0,%1\n" | |
24 | " jo 0b" | |
7a71fd1c | 25 | : "+d" (r0), "+a" (s) : : "cc", "memory"); |
1da177e4 LT |
26 | return (char *) r0; |
27 | } | |
28 | ||
29 | static inline char *__strnend(const char *s, size_t n) | |
30 | { | |
31 | register unsigned long r0 asm("0") = 0; | |
32 | const char *p = s + n; | |
33 | ||
34 | asm volatile ("0: srst %0,%1\n" | |
35 | " jo 0b" | |
7a71fd1c | 36 | : "+d" (p), "+a" (s) : "d" (r0) : "cc", "memory"); |
1da177e4 LT |
37 | return (char *) p; |
38 | } | |
39 | ||
40 | /** | |
41 | * strlen - Find the length of a string | |
42 | * @s: The string to be sized | |
43 | * | |
44 | * returns the length of @s | |
45 | */ | |
46 | size_t strlen(const char *s) | |
47 | { | |
48 | return __strend(s) - s; | |
49 | } | |
50 | EXPORT_SYMBOL(strlen); | |
51 | ||
52 | /** | |
53 | * strnlen - Find the length of a length-limited string | |
54 | * @s: The string to be sized | |
55 | * @n: The maximum number of bytes to search | |
56 | * | |
57 | * returns the minimum of the length of @s and @n | |
58 | */ | |
59 | size_t strnlen(const char * s, size_t n) | |
60 | { | |
61 | return __strnend(s, n) - s; | |
62 | } | |
63 | EXPORT_SYMBOL(strnlen); | |
64 | ||
65 | /** | |
66 | * strcpy - Copy a %NUL terminated string | |
67 | * @dest: Where to copy the string to | |
68 | * @src: Where to copy the string from | |
69 | * | |
70 | * returns a pointer to @dest | |
71 | */ | |
72 | char *strcpy(char *dest, const char *src) | |
73 | { | |
74 | register int r0 asm("0") = 0; | |
75 | char *ret = dest; | |
76 | ||
77 | asm volatile ("0: mvst %0,%1\n" | |
78 | " jo 0b" | |
79 | : "+&a" (dest), "+&a" (src) : "d" (r0) | |
80 | : "cc", "memory" ); | |
81 | return ret; | |
82 | } | |
83 | EXPORT_SYMBOL(strcpy); | |
84 | ||
85 | /** | |
86 | * strlcpy - Copy a %NUL terminated string into a sized buffer | |
87 | * @dest: Where to copy the string to | |
88 | * @src: Where to copy the string from | |
89 | * @size: size of destination buffer | |
90 | * | |
91 | * Compatible with *BSD: the result is always a valid | |
92 | * NUL-terminated string that fits in the buffer (unless, | |
93 | * of course, the buffer size is zero). It does not pad | |
94 | * out the result like strncpy() does. | |
95 | */ | |
96 | size_t strlcpy(char *dest, const char *src, size_t size) | |
97 | { | |
98 | size_t ret = __strend(src) - src; | |
99 | ||
100 | if (size) { | |
101 | size_t len = (ret >= size) ? size-1 : ret; | |
102 | dest[len] = '\0'; | |
535c611d | 103 | memcpy(dest, src, len); |
1da177e4 LT |
104 | } |
105 | return ret; | |
106 | } | |
107 | EXPORT_SYMBOL(strlcpy); | |
108 | ||
109 | /** | |
110 | * strncpy - Copy a length-limited, %NUL-terminated string | |
111 | * @dest: Where to copy the string to | |
112 | * @src: Where to copy the string from | |
113 | * @n: The maximum number of bytes to copy | |
114 | * | |
115 | * The result is not %NUL-terminated if the source exceeds | |
116 | * @n bytes. | |
117 | */ | |
118 | char *strncpy(char *dest, const char *src, size_t n) | |
119 | { | |
120 | size_t len = __strnend(src, n) - src; | |
535c611d HC |
121 | memset(dest + len, 0, n - len); |
122 | memcpy(dest, src, len); | |
1da177e4 LT |
123 | return dest; |
124 | } | |
125 | EXPORT_SYMBOL(strncpy); | |
126 | ||
127 | /** | |
128 | * strcat - Append one %NUL-terminated string to another | |
129 | * @dest: The string to be appended to | |
130 | * @src: The string to append to it | |
131 | * | |
132 | * returns a pointer to @dest | |
133 | */ | |
134 | char *strcat(char *dest, const char *src) | |
135 | { | |
136 | register int r0 asm("0") = 0; | |
137 | unsigned long dummy; | |
138 | char *ret = dest; | |
139 | ||
140 | asm volatile ("0: srst %0,%1\n" | |
141 | " jo 0b\n" | |
142 | "1: mvst %0,%2\n" | |
143 | " jo 1b" | |
144 | : "=&a" (dummy), "+a" (dest), "+a" (src) | |
145 | : "d" (r0), "0" (0UL) : "cc", "memory" ); | |
146 | return ret; | |
147 | } | |
148 | EXPORT_SYMBOL(strcat); | |
149 | ||
150 | /** | |
151 | * strlcat - Append a length-limited, %NUL-terminated string to another | |
152 | * @dest: The string to be appended to | |
153 | * @src: The string to append to it | |
154 | * @n: The size of the destination buffer. | |
155 | */ | |
156 | size_t strlcat(char *dest, const char *src, size_t n) | |
157 | { | |
158 | size_t dsize = __strend(dest) - dest; | |
159 | size_t len = __strend(src) - src; | |
160 | size_t res = dsize + len; | |
161 | ||
162 | if (dsize < n) { | |
163 | dest += dsize; | |
164 | n -= dsize; | |
165 | if (len >= n) | |
166 | len = n - 1; | |
167 | dest[len] = '\0'; | |
535c611d | 168 | memcpy(dest, src, len); |
1da177e4 LT |
169 | } |
170 | return res; | |
171 | } | |
172 | EXPORT_SYMBOL(strlcat); | |
173 | ||
174 | /** | |
175 | * strncat - Append a length-limited, %NUL-terminated string to another | |
176 | * @dest: The string to be appended to | |
177 | * @src: The string to append to it | |
178 | * @n: The maximum numbers of bytes to copy | |
179 | * | |
180 | * returns a pointer to @dest | |
181 | * | |
182 | * Note that in contrast to strncpy, strncat ensures the result is | |
183 | * terminated. | |
184 | */ | |
185 | char *strncat(char *dest, const char *src, size_t n) | |
186 | { | |
187 | size_t len = __strnend(src, n) - src; | |
188 | char *p = __strend(dest); | |
189 | ||
190 | p[len] = '\0'; | |
535c611d | 191 | memcpy(p, src, len); |
1da177e4 LT |
192 | return dest; |
193 | } | |
194 | EXPORT_SYMBOL(strncat); | |
195 | ||
196 | /** | |
197 | * strcmp - Compare two strings | |
198 | * @cs: One string | |
199 | * @ct: Another string | |
200 | * | |
201 | * returns 0 if @cs and @ct are equal, | |
202 | * < 0 if @cs is less than @ct | |
203 | * > 0 if @cs is greater than @ct | |
204 | */ | |
205 | int strcmp(const char *cs, const char *ct) | |
206 | { | |
207 | register int r0 asm("0") = 0; | |
208 | int ret = 0; | |
209 | ||
210 | asm volatile ("0: clst %2,%3\n" | |
211 | " jo 0b\n" | |
212 | " je 1f\n" | |
213 | " ic %0,0(%2)\n" | |
214 | " ic %1,0(%3)\n" | |
215 | " sr %0,%1\n" | |
216 | "1:" | |
217 | : "+d" (ret), "+d" (r0), "+a" (cs), "+a" (ct) | |
7a71fd1c | 218 | : : "cc", "memory"); |
1da177e4 LT |
219 | return ret; |
220 | } | |
221 | EXPORT_SYMBOL(strcmp); | |
222 | ||
223 | /** | |
224 | * strrchr - Find the last occurrence of a character in a string | |
225 | * @s: The string to be searched | |
226 | * @c: The character to search for | |
227 | */ | |
228 | char * strrchr(const char * s, int c) | |
229 | { | |
230 | size_t len = __strend(s) - s; | |
231 | ||
232 | if (len) | |
233 | do { | |
234 | if (s[len] == (char) c) | |
235 | return (char *) s + len; | |
236 | } while (--len > 0); | |
d2c993d8 | 237 | return NULL; |
1da177e4 LT |
238 | } |
239 | EXPORT_SYMBOL(strrchr); | |
240 | ||
db7f5eef | 241 | static inline int clcle(const char *s1, unsigned long l1, |
e2efc424 | 242 | const char *s2, unsigned long l2) |
db7f5eef HC |
243 | { |
244 | register unsigned long r2 asm("2") = (unsigned long) s1; | |
e2efc424 | 245 | register unsigned long r3 asm("3") = (unsigned long) l1; |
db7f5eef HC |
246 | register unsigned long r4 asm("4") = (unsigned long) s2; |
247 | register unsigned long r5 asm("5") = (unsigned long) l2; | |
248 | int cc; | |
249 | ||
250 | asm volatile ("0: clcle %1,%3,0\n" | |
251 | " jo 0b\n" | |
252 | " ipm %0\n" | |
253 | " srl %0,28" | |
254 | : "=&d" (cc), "+a" (r2), "+a" (r3), | |
7a71fd1c | 255 | "+a" (r4), "+a" (r5) : : "cc", "memory"); |
db7f5eef HC |
256 | return cc; |
257 | } | |
258 | ||
1da177e4 LT |
259 | /** |
260 | * strstr - Find the first substring in a %NUL terminated string | |
261 | * @s1: The string to be searched | |
262 | * @s2: The string to search for | |
263 | */ | |
264 | char * strstr(const char * s1,const char * s2) | |
265 | { | |
266 | int l1, l2; | |
267 | ||
268 | l2 = __strend(s2) - s2; | |
269 | if (!l2) | |
270 | return (char *) s1; | |
271 | l1 = __strend(s1) - s1; | |
272 | while (l1-- >= l2) { | |
e2efc424 | 273 | int cc; |
db7f5eef | 274 | |
e2efc424 | 275 | cc = clcle(s1, l2, s2, l2); |
1da177e4 LT |
276 | if (!cc) |
277 | return (char *) s1; | |
278 | s1++; | |
279 | } | |
d2c993d8 | 280 | return NULL; |
1da177e4 LT |
281 | } |
282 | EXPORT_SYMBOL(strstr); | |
283 | ||
284 | /** | |
285 | * memchr - Find a character in an area of memory. | |
286 | * @s: The memory area | |
287 | * @c: The byte to search for | |
288 | * @n: The size of the area. | |
289 | * | |
290 | * returns the address of the first occurrence of @c, or %NULL | |
291 | * if @c is not found | |
292 | */ | |
293 | void *memchr(const void *s, int c, size_t n) | |
294 | { | |
295 | register int r0 asm("0") = (char) c; | |
296 | const void *ret = s + n; | |
297 | ||
298 | asm volatile ("0: srst %0,%1\n" | |
299 | " jo 0b\n" | |
300 | " jl 1f\n" | |
301 | " la %0,0\n" | |
302 | "1:" | |
7a71fd1c | 303 | : "+a" (ret), "+&a" (s) : "d" (r0) : "cc", "memory"); |
1da177e4 LT |
304 | return (void *) ret; |
305 | } | |
306 | EXPORT_SYMBOL(memchr); | |
307 | ||
308 | /** | |
309 | * memcmp - Compare two areas of memory | |
310 | * @cs: One area of memory | |
311 | * @ct: Another area of memory | |
312 | * @count: The size of the area. | |
313 | */ | |
314 | int memcmp(const void *cs, const void *ct, size_t n) | |
315 | { | |
e2efc424 | 316 | int ret; |
1da177e4 | 317 | |
e2efc424 | 318 | ret = clcle(cs, n, ct, n); |
1da177e4 | 319 | if (ret) |
e2efc424 | 320 | ret = ret == 1 ? -1 : 1; |
1da177e4 LT |
321 | return ret; |
322 | } | |
323 | EXPORT_SYMBOL(memcmp); | |
324 | ||
325 | /** | |
326 | * memscan - Find a character in an area of memory. | |
327 | * @s: The memory area | |
328 | * @c: The byte to search for | |
329 | * @n: The size of the area. | |
330 | * | |
331 | * returns the address of the first occurrence of @c, or 1 byte past | |
332 | * the area if @c is not found | |
333 | */ | |
334 | void *memscan(void *s, int c, size_t n) | |
335 | { | |
336 | register int r0 asm("0") = (char) c; | |
337 | const void *ret = s + n; | |
338 | ||
339 | asm volatile ("0: srst %0,%1\n" | |
340 | " jo 0b\n" | |
7a71fd1c | 341 | : "+a" (ret), "+&a" (s) : "d" (r0) : "cc", "memory"); |
1da177e4 LT |
342 | return (void *) ret; |
343 | } | |
344 | EXPORT_SYMBOL(memscan); |