/* Optimized, inlined string functions.  S/390 version.
   Copyright (C) 2000-2016 Free Software Foundation, Inc.
   Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */

#ifndef _STRING_H
# error "Never use <bits/string.h> directly; include <string.h> instead."
#endif

/* Use the unaligned string inline ABI.  */
#define _STRING_INLINE_unaligned 1

/* We only provide optimizations if the user selects them and if
   GNU CC is used.  */
#if !defined __NO_STRING_INLINES && defined __USE_STRING_INLINES \
    && defined __GNUC__ && __GNUC__ >= 2

#ifndef __STRING_INLINE
# ifndef __extern_inline
#  define __STRING_INLINE inline
# else
#  define __STRING_INLINE __extern_inline
# endif
#endif

#define _HAVE_STRING_ARCH_strlen 1
#ifndef _FORCE_INLINES
#define strlen(str) __strlen_g ((str))

__STRING_INLINE size_t __strlen_g (const char *) __asm__ ("strlen");

__STRING_INLINE size_t
__strlen_g (const char *__str)
{
    char *__ptr, *__tmp;

    __ptr = (char *) 0;
    __tmp = (char *) __str;
    __asm__ __volatile__ ("   la    0,0\n"
			  "0: srst  %0,%1\n"
			  "   jo    0b\n"
			  : "+&a" (__ptr), "+&a" (__tmp) :
			  : "cc", "memory", "0" );
    return (size_t) (__ptr - __str);
}
#endif

/* Copy SRC to DEST.  */
#define _HAVE_STRING_ARCH_strcpy 1
#ifndef _FORCE_INLINES
#define strcpy(dest, src) __strcpy_g ((dest), (src))

__STRING_INLINE char *__strcpy_g (char *, const char *) __asm__ ("strcpy");

__STRING_INLINE char *
__strcpy_g (char *__dest, const char *__src)
{
    char *tmp = __dest;

    __asm__ __volatile__ ("   la    0,0\n"
			  "0: mvst  %0,%1\n"
			  "   jo    0b"
			  : "+&a" (__dest), "+&a" (__src) :
			  : "cc", "memory", "0" );
    return tmp;
}
#endif

#define _HAVE_STRING_ARCH_strncpy 1
#ifndef _FORCE_INLINES
#define strncpy(dest, src, n) __strncpy_g ((dest), (src), (n))

__STRING_INLINE char *__strncpy_g (char *, const char *, size_t)
     __asm__ ("strncpy");

__STRING_INLINE char *
__strncpy_g (char *__dest, const char *__src, size_t __n)
{
    char *__ret = __dest;
    char *__ptr;
    size_t __diff;

    if (__n > 0) {
      __diff = (size_t) (__dest - __src);
      __ptr = (char *) __src;
      __asm__ __volatile__ ("   j     1f\n"
			    "0: la    %0,1(%0)\n"
			    "1: icm   0,1,0(%0)\n"
			    "   stc   0,0(%2,%0)\n"
			    "   jz    3f\n"
#if defined(__s390x__)
			    "   brctg %1,0b\n"
#else
			    "   brct  %1,0b\n"
#endif
			    "   j     4f\n"
			    "2: la    %0,1(%0)\n"
			    "   stc   0,0(%2,%0)\n"
#if defined(__s390x__)
			    "3: brctg %1,2b\n"
#else
			    "3: brct  %1,2b\n"
#endif
			    "4:"
			    : "+&a" (__ptr), "+&a" (__n) : "a" (__diff)
			    : "cc", "memory", "0" );
    }
    return __ret;
}
#endif

/* Append SRC onto DEST.  */
#define _HAVE_STRING_ARCH_strcat 1
#ifndef _FORCE_INLINES
#define strcat(dest, src) __strcat_g ((dest), (src))

__STRING_INLINE char *__strcat_g (char *, const char *) __asm__ ("strcat");

__STRING_INLINE char *
__strcat_g (char *__dest, const char *__src)
{
    char *__ret = __dest;
    char *__ptr, *__tmp;

    /* Move __ptr to the end of __dest.  */
    __ptr = (char *) 0;
    __tmp = __dest;
    __asm__ __volatile__ ("   la    0,0\n"
			  "0: srst  %0,%1\n"
			  "   jo    0b\n"
			  : "+&a" (__ptr), "+&a" (__tmp) :
			  : "cc", "0" );

    /* Now do the copy.  */
    __asm__ __volatile__ ("   la    0,0\n"
			  "0: mvst  %0,%1\n"
			  "   jo    0b"
			  : "+&a" (__ptr), "+&a" (__src) :
			  : "cc", "memory", "0" );
    return __ret;
}
#endif

/* Append no more than N characters from SRC onto DEST.  */
#define _HAVE_STRING_ARCH_strncat 1
#ifndef _FORCE_INLINES
#define strncat(dest, src, n) __strncat_g ((dest), (src), (n))

__STRING_INLINE char *__strncat_g (char *, const char *, size_t)
     __asm__ ("strncat");

__STRING_INLINE char *
__strncat_g (char *__dest, const char *__src, size_t __n)
{
    char *__ret = __dest;
    char *__ptr, *__tmp;
    size_t __diff;

    if (__n > 0) {
      /* Move __ptr to the end of __dest.  */
      __ptr = (char *) 0;
      __tmp = __dest;
      __asm__ __volatile__ ("   la    0,0\n"
			    "0: srst  %0,%1\n"
			  "   jo    0b\n"
			    : "+&a" (__ptr), "+&a" (__tmp) :
			    : "cc", "memory", "0" );

      __diff = (size_t) (__ptr - __src);
      __tmp = (char *) __src;
      __asm__ __volatile__ ("   j     1f\n"
			    "0: la    %0,1(%0)\n"
			    "1: icm   0,1,0(%0)\n"
			    "   stc   0,0(%2,%0)\n"
			    "   jz    2f\n"
#if defined(__s390x__)
			    "   brctg %1,0b\n"
#else
			    "   brct  %1,0b\n"
#endif
			    "   slr   0,0\n"
			    "   stc   0,1(%2,%0)\n"
			    "2:"
			    : "+&a" (__tmp), "+&a" (__n) : "a" (__diff)
			    : "cc", "memory", "0" );

    }
    return __ret;
}
#endif

/* Search N bytes of S for C.  */
#define _HAVE_STRING_ARCH_memchr 1
#ifndef _FORCE_INLINES
__STRING_INLINE void *
memchr (const void *__str, int __c, size_t __n)
{
    char *__ptr, *__tmp;

    __tmp = (char *) __str;
    __ptr = (char *) __tmp + __n;
    __asm__ __volatile__ ("   lhi   0,0xff\n"
			  "   nr    0,%2\n"
			  "0: srst  %0,%1\n"
			  "   jo    0b\n"
			  "   brc   13,1f\n"
			  "   la    %0,0\n"
			  "1:"
			  : "+&a" (__ptr), "+&a" (__tmp) : "d" (__c)
			  : "cc", "memory", "0" );
    return __ptr;
}
#endif

/* Compare S1 and S2.  */
#define _HAVE_STRING_ARCH_strcmp 1
#ifndef _FORCE_INLINES
__STRING_INLINE int
strcmp (const char *__s1, const char *__s2)
{
    char *__p1, *__p2;
    int __ret;

    __p1 = (char *) __s1;
    __p2 = (char *) __s2;
    __asm__ __volatile__ ("   slr   0,0\n"
			  "0: clst  %1,%2\n"
			  "   jo    0b\n"
			  "   ipm   %0\n"
			  "   srl   %0,28"
			  : "=d" (__ret), "+&a" (__p1), "+&a" (__p2) :
			  : "cc", "memory", "0" );
    __ret = (__ret == 0) ? 0 : (__ret == 1) ? -1 : 1;
    return __ret;
}
#endif

#endif	/* Use string inlines && GNU CC.  */
