/* Optimized, inlined string functions.  S/390 version.
   Copyright (C) 2000, 2001 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, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

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

/* The s390 processors can access unaligned multi-byte variables.  */
#define _STRING_ARCH_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
# ifdef __cplusplus
#  define __STRING_INLINE inline
# else
#  define __STRING_INLINE extern __inline
# endif
#endif

#define _HAVE_STRING_ARCH_strlen 1
#ifndef _FORCE_INLINES
__STRING_INLINE size_t
strlen (__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", "0" );
    return (size_t) (__ptr - __str);
}
#endif

/* Copy SRC to DEST.  */
#define _HAVE_STRING_ARCH_strcpy 1
#ifndef _FORCE_INLINES
__STRING_INLINE char *
strcpy (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
__STRING_INLINE char *
strncpy (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", "0" );
    }
    return __ret;
}
#endif

/* Append SRC onto DEST.  */
#define _HAVE_STRING_ARCH_strcat 1
#ifndef _FORCE_INLINES
__STRING_INLINE char *
strcat(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", "0" );
    return __ret;
}
#endif

/* Append no more than N characters from SRC onto DEST.  */
#define _HAVE_STRING_ARCH_strncat 1
#ifndef _FORCE_INLINES
__STRING_INLINE char *
strncat (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", "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", "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", "0" );
    return __ptr;
}
#endif

/* Search N bytes of S for C.  */
#define _HAVE_STRING_ARCH_memchr 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", "0" );
    __ret = (__ret == 0) ? 0 : (__ret == 1) ? -1 : 1;
    return __ret;
}
#endif

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