mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-12 16:20:06 +00:00
string: Improve generic stpcpy
It follows the strategy: - Align the destination on word boundary using byte operations. - If source is also word aligned, read a word per time, check for null (using has_zero from string-fzb.h), and write the remaining bytes. - If source is not word aligned, loop by aligning the source, and merging the result of two reads. Similar to aligned case, check for null with has_zero, and write the remaining bytes if null is found. Checked on x86_64-linux-gnu, i686-linux-gnu, powerpc64-linux-gnu, and powerpc-linux-gnu by removing the arch-specific assembly implementation and disabling multi-arch (it covers both LE and BE for 64 and 32 bits). Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
This commit is contained in:
parent
367c31b5d6
commit
30c1dfde31
@ -15,12 +15,12 @@
|
|||||||
License along with the GNU C Library; if not, see
|
License along with the GNU C Library; if not, see
|
||||||
<https://www.gnu.org/licenses/>. */
|
<https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define NO_MEMPCPY_STPCPY_REDIRECT
|
#define NO_MEMPCPY_STPCPY_REDIRECT
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <memcopy.h>
|
||||||
|
#include <string-fzb.h>
|
||||||
|
#include <string-misc.h>
|
||||||
|
|
||||||
#undef __stpcpy
|
#undef __stpcpy
|
||||||
#undef stpcpy
|
#undef stpcpy
|
||||||
@ -29,12 +29,92 @@
|
|||||||
# define STPCPY __stpcpy
|
# define STPCPY __stpcpy
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static __always_inline char *
|
||||||
|
write_byte_from_word (op_t *dest, op_t word)
|
||||||
|
{
|
||||||
|
char *d = (char *) dest;
|
||||||
|
for (size_t i = 0; i < OPSIZ; i++, ++d)
|
||||||
|
{
|
||||||
|
char c = extractbyte (word, i);
|
||||||
|
*d = c;
|
||||||
|
if (c == '\0')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline char *
|
||||||
|
stpcpy_aligned_loop (op_t *restrict dst, const op_t *restrict src)
|
||||||
|
{
|
||||||
|
op_t word;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
word = *src++;
|
||||||
|
if (has_zero (word))
|
||||||
|
break;
|
||||||
|
*dst++ = word;
|
||||||
|
}
|
||||||
|
|
||||||
|
return write_byte_from_word (dst, word);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline char *
|
||||||
|
stpcpy_unaligned_loop (op_t *restrict dst, const op_t *restrict src,
|
||||||
|
uintptr_t ofs)
|
||||||
|
{
|
||||||
|
op_t w2a = *src++;
|
||||||
|
uintptr_t sh_1 = ofs * CHAR_BIT;
|
||||||
|
uintptr_t sh_2 = OPSIZ * CHAR_BIT - sh_1;
|
||||||
|
|
||||||
|
op_t w2 = MERGE (w2a, sh_1, (op_t)-1, sh_2);
|
||||||
|
if (!has_zero (w2))
|
||||||
|
{
|
||||||
|
op_t w2b;
|
||||||
|
|
||||||
|
/* Unaligned loop. The invariant is that W2B, which is "ahead" of W1,
|
||||||
|
does not contain end-of-string. Therefore it is safe (and necessary)
|
||||||
|
to read another word from each while we do not have a difference. */
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
w2b = *src++;
|
||||||
|
w2 = MERGE (w2a, sh_1, w2b, sh_2);
|
||||||
|
/* Check if there is zero on w2a. */
|
||||||
|
if (has_zero (w2))
|
||||||
|
goto out;
|
||||||
|
*dst++ = w2;
|
||||||
|
if (has_zero (w2b))
|
||||||
|
break;
|
||||||
|
w2a = w2b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Align the final partial of P2. */
|
||||||
|
w2 = MERGE (w2b, sh_1, 0, sh_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
return write_byte_from_word (dst, w2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Copy SRC to DEST, returning the address of the terminating '\0' in DEST. */
|
/* Copy SRC to DEST, returning the address of the terminating '\0' in DEST. */
|
||||||
char *
|
char *
|
||||||
STPCPY (char *dest, const char *src)
|
STPCPY (char *dest, const char *src)
|
||||||
{
|
{
|
||||||
size_t len = strlen (src);
|
/* Copy just a few bytes to make DEST aligned. */
|
||||||
return memcpy (dest, src, len + 1) + len;
|
size_t len = (-(uintptr_t) dest) % OPSIZ;
|
||||||
|
for (; len != 0; len--, ++dest)
|
||||||
|
{
|
||||||
|
char c = *src++;
|
||||||
|
*dest = c;
|
||||||
|
if (c == '\0')
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DEST is now aligned to op_t, SRC may or may not be. */
|
||||||
|
uintptr_t ofs = (uintptr_t) src % OPSIZ;
|
||||||
|
return ofs == 0 ? stpcpy_aligned_loop ((op_t*) dest, (const op_t *) src)
|
||||||
|
: stpcpy_unaligned_loop ((op_t*) dest,
|
||||||
|
(const op_t *) (src - ofs) , ofs);
|
||||||
}
|
}
|
||||||
weak_alias (__stpcpy, stpcpy)
|
weak_alias (__stpcpy, stpcpy)
|
||||||
libc_hidden_def (__stpcpy)
|
libc_hidden_def (__stpcpy)
|
||||||
|
Loading…
Reference in New Issue
Block a user