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