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