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        ssize_t len = __strend(s) - s;
 263
 264        do {
 265                if (s[len] == (char)c)
 266                        return (char *)s + len;
 267        } while (--len >= 0);
 268        return NULL;
 269}
 270EXPORT_SYMBOL(strrchr);
 271#endif
 272
 273static inline int clcle(const char *s1, unsigned long l1,
 274                        const char *s2, unsigned long l2)
 275{
 276        union register_pair r1 = { .even = (unsigned long)s1, .odd = l1, };
 277        union register_pair r3 = { .even = (unsigned long)s2, .odd = l2, };
 278        int cc;
 279
 280        asm volatile(
 281                "0:     clcle   %[r1],%[r3],0\n"
 282                "       jo      0b\n"
 283                "       ipm     %[cc]\n"
 284                "       srl     %[cc],28\n"
 285                : [cc] "=&d" (cc), [r1] "+&d" (r1.pair), [r3] "+&d" (r3.pair)
 286                :
 287                : "cc", "memory");
 288        return cc;
 289}
 290
 291/**
 292 * strstr - Find the first substring in a %NUL terminated string
 293 * @s1: The string to be searched
 294 * @s2: The string to search for
 295 */
 296#ifdef __HAVE_ARCH_STRSTR
 297char *strstr(const char *s1, const char *s2)
 298{
 299        int l1, l2;
 300
 301        l2 = __strend(s2) - s2;
 302        if (!l2)
 303                return (char *) s1;
 304        l1 = __strend(s1) - s1;
 305        while (l1-- >= l2) {
 306                int cc;
 307
 308                cc = clcle(s1, l2, s2, l2);
 309                if (!cc)
 310                        return (char *) s1;
 311                s1++;
 312        }
 313        return NULL;
 314}
 315EXPORT_SYMBOL(strstr);
 316#endif
 317
 318/**
 319 * memchr - Find a character in an area of memory.
 320 * @s: The memory area
 321 * @c: The byte to search for
 322 * @n: The size of the area.
 323 *
 324 * returns the address of the first occurrence of @c, or %NULL
 325 * if @c is not found
 326 */
 327#ifdef __HAVE_ARCH_MEMCHR
 328void *memchr(const void *s, int c, size_t n)
 329{
 330        const void *ret = s + n;
 331
 332        asm volatile(
 333                "       lgr     0,%[c]\n"
 334                "0:     srst    %[ret],%[s]\n"
 335                "       jo      0b\n"
 336                "       jl      1f\n"
 337                "       la      %[ret],0\n"
 338                "1:"
 339                : [ret] "+&a" (ret), [s] "+&a" (s)
 340                : [c] "d" (c)
 341                : "cc", "memory", "0");
 342        return (void *) ret;
 343}
 344EXPORT_SYMBOL(memchr);
 345#endif
 346
 347/**
 348 * memcmp - Compare two areas of memory
 349 * @s1: One area of memory
 350 * @s2: Another area of memory
 351 * @n: The size of the area.
 352 */
 353#ifdef __HAVE_ARCH_MEMCMP
 354int memcmp(const void *s1, const void *s2, size_t n)
 355{
 356        int ret;
 357
 358        ret = clcle(s1, n, s2, n);
 359        if (ret)
 360                ret = ret == 1 ? -1 : 1;
 361        return ret;
 362}
 363EXPORT_SYMBOL(memcmp);
 364#endif
 365
 366/**
 367 * memscan - Find a character in an area of memory.
 368 * @s: The memory area
 369 * @c: The byte to search for
 370 * @n: The size of the area.
 371 *
 372 * returns the address of the first occurrence of @c, or 1 byte past
 373 * the area if @c is not found
 374 */
 375#ifdef __HAVE_ARCH_MEMSCAN
 376void *memscan(void *s, int c, size_t n)
 377{
 378        const void *ret = s + n;
 379
 380        asm volatile(
 381                "       lgr     0,%[c]\n"
 382                "0:     srst    %[ret],%[s]\n"
 383                "       jo      0b\n"
 384                : [ret] "+&a" (ret), [s] "+&a" (s)
 385                : [c] "d" (c)
 386                : "cc", "memory", "0");
 387        return (void *)ret;
 388}
 389EXPORT_SYMBOL(memscan);
 390#endif
 391