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