<string.h>: Make strchrnul, strcasestr, memmem available by default
[platform/upstream/glibc.git] / string / stpcpy.c
1 /* Copyright (C) 1992-2023 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17
18 #define NO_MEMPCPY_STPCPY_REDIRECT
19 #include <string.h>
20 #include <stdint.h>
21 #include <memcopy.h>
22 #include <string-fzb.h>
23 #include <string-misc.h>
24
25 #undef __stpcpy
26 #undef stpcpy
27
28 #ifndef STPCPY
29 # define STPCPY __stpcpy
30 #endif
31
32 static __always_inline char *
33 write_byte_from_word (op_t *dest, op_t word)
34 {
35   char *d = (char *) dest;
36   for (size_t i = 0; i < OPSIZ; i++, ++d)
37     {
38       char c = extractbyte (word, i);
39       *d = c;
40       if (c == '\0')
41         break;
42     }
43   return d;
44 }
45
46 static __always_inline char *
47 stpcpy_aligned_loop (op_t *restrict dst, const op_t *restrict src)
48 {
49   op_t word;
50   while (1)
51     {
52       word = *src++;
53       if (has_zero (word))
54         break;
55       *dst++ = word;
56     }
57
58   return write_byte_from_word (dst, word);
59 }
60
61 static __always_inline char *
62 stpcpy_unaligned_loop (op_t *restrict dst, const op_t *restrict src,
63                        uintptr_t ofs)
64 {
65   op_t w2a = *src++;
66   uintptr_t sh_1 = ofs * CHAR_BIT;
67   uintptr_t sh_2 = OPSIZ * CHAR_BIT - sh_1;
68
69   op_t w2 = MERGE (w2a, sh_1, (op_t)-1, sh_2);
70   if (!has_zero (w2))
71     {
72       op_t w2b;
73
74       /* Unaligned loop.  The invariant is that W2B, which is "ahead" of W1,
75          does not contain end-of-string.  Therefore it is safe (and necessary)
76          to read another word from each while we do not have a difference.  */
77       while (1)
78         {
79           w2b = *src++;
80           w2 = MERGE (w2a, sh_1, w2b, sh_2);
81           /* Check if there is zero on w2a.  */
82           if (has_zero (w2))
83             goto out;
84           *dst++ = w2;
85           if (has_zero (w2b))
86             break;
87           w2a = w2b;
88         }
89
90       /* Align the final partial of P2.  */
91       w2 = MERGE (w2b, sh_1, 0, sh_2);
92     }
93
94 out:
95   return write_byte_from_word (dst, w2);
96 }
97
98
99 /* Copy SRC to DEST, returning the address of the terminating '\0' in DEST.  */
100 char *
101 STPCPY (char *dest, const char *src)
102 {
103   /* Copy just a few bytes to make DEST aligned.  */
104   size_t len = (-(uintptr_t) dest) % OPSIZ;
105   for (; len != 0; len--, ++dest)
106     {
107       char c = *src++;
108       *dest = c;
109       if (c == '\0')
110         return dest;
111     }
112
113   /* DEST is now aligned to op_t, SRC may or may not be.  */
114   uintptr_t ofs = (uintptr_t) src % OPSIZ;
115   return ofs == 0 ? stpcpy_aligned_loop ((op_t*) dest, (const op_t *) src)
116                   : stpcpy_unaligned_loop ((op_t*) dest,
117                                            (const op_t *) (src - ofs) , ofs);
118 }
119 weak_alias (__stpcpy, stpcpy)
120 libc_hidden_def (__stpcpy)
121 libc_hidden_builtin_def (stpcpy)