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