linux/arch/s390/lib/string.c
<<
>>
Prefs
   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 */
  19static 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
  29static 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 */
  46size_t strlen(const char *s)
  47{
  48        return __strend(s) - s;
  49}
  50EXPORT_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 */
  59size_t strnlen(const char * s, size_t n)
  60{
  61        return __strnend(s, n) - s;
  62}
  63EXPORT_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 */
  72char *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}
  83EXPORT_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 */
  96size_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}
 107EXPORT_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 */
 118char *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}
 125EXPORT_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 */
 134char *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}
 148EXPORT_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 */
 156size_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}
 172EXPORT_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 */
 185char *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}
 194EXPORT_SYMBOL(strncat);
 195
 196/**
 197 * strcmp - Compare two strings
 198 * @cs: One string
 199 * @ct: Another string
 200 *
 201 * returns   0 if @cs and @ct are equal,
 202 *         < 0 if @cs is less than @ct
 203 *         > 0 if @cs is greater than @ct
 204 */
 205int strcmp(const char *cs, const char *ct)
 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" (cs), "+a" (ct)
 218                      : : "cc", "memory");
 219        return ret;
 220}
 221EXPORT_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 */
 228char * 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}
 239EXPORT_SYMBOL(strrchr);
 240
 241static 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 */
 264char * 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}
 282EXPORT_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 */
 293void *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}
 306EXPORT_SYMBOL(memchr);
 307
 308/**
 309 * memcmp - Compare two areas of memory
 310 * @cs: One area of memory
 311 * @ct: Another area of memory
 312 * @count: The size of the area.
 313 */
 314int memcmp(const void *cs, const void *ct, size_t n)
 315{
 316        int ret;
 317
 318        ret = clcle(cs, n, ct, n);
 319        if (ret)
 320                ret = ret == 1 ? -1 : 1;
 321        return ret;
 322}
 323EXPORT_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 */
 334void *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}
 344EXPORT_SYMBOL(memscan);
 345