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