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