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 */ 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 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 size_t strlen(const char *s) 47 { 48 return __strend(s) - s; 49 } 50 EXPORT_SYMBOL(strlen); 51 52 /** 53 * strnlen - Find the length of a length-limited string 54 * @s: The string to be sized 55 * @n: The maximum number of bytes to search 56 * 57 * returns the minimum of the length of @s and @n 58 */ 59 size_t strnlen(const char *s, size_t n) 60 { 61 return __strnend(s, n) - s; 62 } 63 EXPORT_SYMBOL(strnlen); 64 65 /** 66 * strcpy - Copy a %NUL terminated string 67 * @dest: Where to copy the string to 68 * @src: Where to copy the string from 69 * 70 * returns a pointer to @dest 71 */ 72 char *strcpy(char *dest, const char *src) 73 { 74 register int r0 asm("0") = 0; 75 char *ret = dest; 76 77 asm volatile ("0: mvst %0,%1\n" 78 " jo 0b" 79 : "+&a" (dest), "+&a" (src) : "d" (r0) 80 : "cc", "memory" ); 81 return ret; 82 } 83 EXPORT_SYMBOL(strcpy); 84 85 /** 86 * strlcpy - Copy a %NUL terminated string into a sized buffer 87 * @dest: Where to copy the string to 88 * @src: Where to copy the string from 89 * @size: size of destination buffer 90 * 91 * Compatible with *BSD: the result is always a valid 92 * NUL-terminated string that fits in the buffer (unless, 93 * of course, the buffer size is zero). It does not pad 94 * out the result like strncpy() does. 95 */ 96 size_t strlcpy(char *dest, const char *src, size_t size) 97 { 98 size_t ret = __strend(src) - src; 99 100 if (size) { 101 size_t len = (ret >= size) ? size-1 : ret; 102 dest[len] = '\0'; 103 memcpy(dest, src, len); 104 } 105 return ret; 106 } 107 EXPORT_SYMBOL(strlcpy); 108 109 /** 110 * strncpy - Copy a length-limited, %NUL-terminated string 111 * @dest: Where to copy the string to 112 * @src: Where to copy the string from 113 * @n: The maximum number of bytes to copy 114 * 115 * The result is not %NUL-terminated if the source exceeds 116 * @n bytes. 117 */ 118 char *strncpy(char *dest, const char *src, size_t n) 119 { 120 size_t len = __strnend(src, n) - src; 121 memset(dest + len, 0, n - len); 122 memcpy(dest, src, len); 123 return dest; 124 } 125 EXPORT_SYMBOL(strncpy); 126 127 /** 128 * strcat - Append one %NUL-terminated string to another 129 * @dest: The string to be appended to 130 * @src: The string to append to it 131 * 132 * returns a pointer to @dest 133 */ 134 char *strcat(char *dest, const char *src) 135 { 136 register int r0 asm("0") = 0; 137 unsigned long dummy; 138 char *ret = dest; 139 140 asm volatile ("0: srst %0,%1\n" 141 " jo 0b\n" 142 "1: mvst %0,%2\n" 143 " jo 1b" 144 : "=&a" (dummy), "+a" (dest), "+a" (src) 145 : "d" (r0), "0" (0UL) : "cc", "memory" ); 146 return ret; 147 } 148 EXPORT_SYMBOL(strcat); 149 150 /** 151 * strlcat - Append a length-limited, %NUL-terminated string to another 152 * @dest: The string to be appended to 153 * @src: The string to append to it 154 * @n: The size of the destination buffer. 155 */ 156 size_t strlcat(char *dest, const char *src, size_t n) 157 { 158 size_t dsize = __strend(dest) - dest; 159 size_t len = __strend(src) - src; 160 size_t res = dsize + len; 161 162 if (dsize < n) { 163 dest += dsize; 164 n -= dsize; 165 if (len >= n) 166 len = n - 1; 167 dest[len] = '\0'; 168 memcpy(dest, src, len); 169 } 170 return res; 171 } 172 EXPORT_SYMBOL(strlcat); 173 174 /** 175 * strncat - Append a length-limited, %NUL-terminated string to another 176 * @dest: The string to be appended to 177 * @src: The string to append to it 178 * @n: The maximum numbers of bytes to copy 179 * 180 * returns a pointer to @dest 181 * 182 * Note that in contrast to strncpy, strncat ensures the result is 183 * terminated. 184 */ 185 char *strncat(char *dest, const char *src, size_t n) 186 { 187 size_t len = __strnend(src, n) - src; 188 char *p = __strend(dest); 189 190 p[len] = '\0'; 191 memcpy(p, src, len); 192 return dest; 193 } 194 EXPORT_SYMBOL(strncat); 195 196 /** 197 * strcmp - Compare two strings 198 * @s1: One string 199 * @s2: Another string 200 * 201 * returns 0 if @s1 and @s2 are equal, 202 * < 0 if @s1 is less than @s2 203 * > 0 if @s1 is greater than @s2 204 */ 205 int strcmp(const char *s1, const char *s2) 206 { 207 register int r0 asm("0") = 0; 208 int ret = 0; 209 210 asm volatile ("0: clst %2,%3\n" 211 " jo 0b\n" 212 " je 1f\n" 213 " ic %0,0(%2)\n" 214 " ic %1,0(%3)\n" 215 " sr %0,%1\n" 216 "1:" 217 : "+d" (ret), "+d" (r0), "+a" (s1), "+a" (s2) 218 : : "cc", "memory"); 219 return ret; 220 } 221 EXPORT_SYMBOL(strcmp); 222 223 /** 224 * strrchr - Find the last occurrence of a character in a string 225 * @s: The string to be searched 226 * @c: The character to search for 227 */ 228 char *strrchr(const char *s, int c) 229 { 230 size_t len = __strend(s) - s; 231 232 if (len) 233 do { 234 if (s[len] == (char) c) 235 return (char *) s + len; 236 } while (--len > 0); 237 return NULL; 238 } 239 EXPORT_SYMBOL(strrchr); 240 241 static inline int clcle(const char *s1, unsigned long l1, 242 const char *s2, unsigned long l2) 243 { 244 register unsigned long r2 asm("2") = (unsigned long) s1; 245 register unsigned long r3 asm("3") = (unsigned long) l1; 246 register unsigned long r4 asm("4") = (unsigned long) s2; 247 register unsigned long r5 asm("5") = (unsigned long) l2; 248 int cc; 249 250 asm volatile ("0: clcle %1,%3,0\n" 251 " jo 0b\n" 252 " ipm %0\n" 253 " srl %0,28" 254 : "=&d" (cc), "+a" (r2), "+a" (r3), 255 "+a" (r4), "+a" (r5) : : "cc", "memory"); 256 return cc; 257 } 258 259 /** 260 * strstr - Find the first substring in a %NUL terminated string 261 * @s1: The string to be searched 262 * @s2: The string to search for 263 */ 264 char *strstr(const char *s1, const char *s2) 265 { 266 int l1, l2; 267 268 l2 = __strend(s2) - s2; 269 if (!l2) 270 return (char *) s1; 271 l1 = __strend(s1) - s1; 272 while (l1-- >= l2) { 273 int cc; 274 275 cc = clcle(s1, l2, s2, l2); 276 if (!cc) 277 return (char *) s1; 278 s1++; 279 } 280 return NULL; 281 } 282 EXPORT_SYMBOL(strstr); 283 284 /** 285 * memchr - Find a character in an area of memory. 286 * @s: The memory area 287 * @c: The byte to search for 288 * @n: The size of the area. 289 * 290 * returns the address of the first occurrence of @c, or %NULL 291 * if @c is not found 292 */ 293 void *memchr(const void *s, int c, size_t n) 294 { 295 register int r0 asm("0") = (char) c; 296 const void *ret = s + n; 297 298 asm volatile ("0: srst %0,%1\n" 299 " jo 0b\n" 300 " jl 1f\n" 301 " la %0,0\n" 302 "1:" 303 : "+a" (ret), "+&a" (s) : "d" (r0) : "cc", "memory"); 304 return (void *) ret; 305 } 306 EXPORT_SYMBOL(memchr); 307 308 /** 309 * memcmp - Compare two areas of memory 310 * @s1: One area of memory 311 * @s2: Another area of memory 312 * @count: The size of the area. 313 */ 314 int memcmp(const void *s1, const void *s2, size_t n) 315 { 316 int ret; 317 318 ret = clcle(s1, n, s2, n); 319 if (ret) 320 ret = ret == 1 ? -1 : 1; 321 return ret; 322 } 323 EXPORT_SYMBOL(memcmp); 324 325 /** 326 * memscan - Find a character in an area of memory. 327 * @s: The memory area 328 * @c: The byte to search for 329 * @n: The size of the area. 330 * 331 * returns the address of the first occurrence of @c, or 1 byte past 332 * the area if @c is not found 333 */ 334 void *memscan(void *s, int c, size_t n) 335 { 336 register int r0 asm("0") = (char) c; 337 const void *ret = s + n; 338 339 asm volatile ("0: srst %0,%1\n" 340 " jo 0b\n" 341 : "+a" (ret), "+&a" (s) : "d" (r0) : "cc", "memory"); 342 return (void *) ret; 343 } 344 EXPORT_SYMBOL(memscan); 345