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