diff options
author | Manuel Novoa III <mjn3@codepoet.org> | 2004-09-02 14:39:38 +0000 |
---|---|---|
committer | Manuel Novoa III <mjn3@codepoet.org> | 2004-09-02 14:39:38 +0000 |
commit | bec90733b9de6e7d75b8bda19b3f0a7117e6b78d (patch) | |
tree | 474ca0f3b80b7990ff33e547d42c1e7dc3014e3b | |
parent | c4cedfc718426337b0215c256c184d2b4e20cd06 (diff) |
Add a couple of mips-specific string funcs.
Port the generic optimized string funcs from glibc, with some tweaks
to cut their size a little. The main change is making memmove
call memcpy for forward copying to trim redundant code.
Make use of both the generic and arch-specific speed-optimized string
funcs configurable. Arch-specific take precedence over generic,
and generic takes precedence over basic size-optimized uClibc funcs.
35 files changed, 3726 insertions, 2 deletions
diff --git a/extra/Configs/Config.in b/extra/Configs/Config.in index 8d4d80760..4deed3026 100644 --- a/extra/Configs/Config.in +++ b/extra/Configs/Config.in @@ -525,6 +525,26 @@ endmenu menu "String and Stdio Support" +config UCLIBC_HAS_STRING_GENERIC_OPT + bool "Use glibc generic string functions" + default y + help + Answer Y to use the (tweaked) glibc generic string functions. + In general, they are faster (but 3-5K larger) than the base + uClibc string functions which are optimized solely for size. + + Many people will answer Y. + +config UCLIBC_HAS_STRING_ARCH_OPT + bool "Use arch-specific string functions" + default y + help + Answer Y to use the arch-specific string functions instead of the + base uClibc versions, which are optimized exclusively for size. + + Most people will answer Y, as this has been default behavior + for some time. + config UCLIBC_HAS_CTYPE_TABLES bool "Use Table Versions Of 'ctype.h' Functions." default y diff --git a/libc/string/Makefile b/libc/string/Makefile index 37a57cc26..02c86d6aa 100644 --- a/libc/string/Makefile +++ b/libc/string/Makefile @@ -20,10 +20,18 @@ TOPDIR=../../ include $(TOPDIR)Rules.mak DIRS= + +ifeq ($(UCLIBC_HAS_STRING_GENERIC_OPT),y) +DIRS += generic +endif + +ifeq ($(UCLIBC_HAS_STRING_ARCH_OPT),y) ifeq ($(TARGET_ARCH),$(wildcard $(TARGET_ARCH))) -DIRS = $(TARGET_ARCH) +DIRS += $(TARGET_ARCH) endif -ALL_SUBDIRS = arm frv i386 sh64 powerpc +endif + +ALL_SUBDIRS = generic arm frv i386 sh64 powerpc mips MSRC= wstring.c MOBJ= basename.o bcopy.o bzero.o dirname.o ffs.o memccpy.o memchr.o memcmp.o \ diff --git a/libc/string/generic/Makefile b/libc/string/generic/Makefile new file mode 100644 index 000000000..3f9af72a3 --- /dev/null +++ b/libc/string/generic/Makefile @@ -0,0 +1,48 @@ +# Makefile for uClibc +# +# Copyright (C) 2000-2003 Erik Andersen <andersen@uclibc.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Library General Public License as published by the Free +# Software Foundation; either version 2 of the License, or (at your option) any +# later version. +# +# This program 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 Library General Public License for more +# details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +TOPDIR=../../../ +include $(TOPDIR)Rules.mak + +CSRC= memchr.c memcmp.c memcpy.c memmem.c memmove.c mempcpy.c memrchr.c \ + memset.c rawmemchr.c strcat.c strchr.c strchrnul.c strcmp.c strcpy.c \ + strcspn.c strlen.c strncat.c strncmp.c strncpy.c strnlen.c \ + strrchr.c strsep.c strspn.c strstr.c strtok_r.c + +COBJS=$(patsubst %.c,%.o, $(CSRC)) + +OBJS=$(COBJS) + +all: $(OBJS) $(LIBC) + +$(LIBC): ar-target + +ar-target: $(OBJS) + $(AR) $(ARFLAGS) $(LIBC) $(OBJS) + +# $(MOBJ): $(MSRC) +# $(CC) $(CFLAGS) -DL_$* $< -c -o $*.o +# $(STRIPTOOL) -x -R .note -R .comment $*.o + +$(COBJS): %.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ + $(STRIPTOOL) -x -R .note -R .comment $*.o + +clean: + $(RM) *.[oa] *~ core + diff --git a/libc/string/generic/bp-checks.h b/libc/string/generic/bp-checks.h new file mode 100644 index 000000000..efbb84716 --- /dev/null +++ b/libc/string/generic/bp-checks.h @@ -0,0 +1,129 @@ +/* Bounded-pointer checking macros for C. + Copyright (C) 2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Greg McGary <greg@mcgary.org> + + 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 _bp_checks_h_ +#define _bp_checks_h_ 1 + +#if __BOUNDED_POINTERS__ + +# define BOUNDS_VIOLATED (__builtin_trap (), 0) + +/* Verify that pointer's value >= low. Return pointer value. */ +# define CHECK_BOUNDS_LOW(ARG) \ + (((__ptrvalue (ARG) < __ptrlow (ARG)) && BOUNDS_VIOLATED), \ + __ptrvalue (ARG)) + +/* Verify that pointer's value < high. Return pointer value. */ +# define CHECK_BOUNDS_HIGH(ARG) \ + (((__ptrvalue (ARG) > __ptrhigh (ARG)) && BOUNDS_VIOLATED), \ + __ptrvalue (ARG)) + +# define _CHECK_N(ARG, N, COND) \ + (((COND) \ + && (__ptrvalue (ARG) < __ptrlow (ARG) \ + || __ptrvalue (ARG) + (N) > __ptrhigh (ARG)) \ + && BOUNDS_VIOLATED), \ + __ptrvalue (ARG)) + +extern void *__unbounded __ubp_memchr (const void *__unbounded, int, unsigned); + +# define _CHECK_STRING(ARG, COND) \ + (((COND) \ + && (__ptrvalue (ARG) < __ptrlow (ARG) \ + || !__ubp_memchr (__ptrvalue (ARG), '\0', \ + (__ptrhigh (ARG) - __ptrvalue (ARG)))) \ + && BOUNDS_VIOLATED), \ + __ptrvalue (ARG)) + +/* Check bounds of a pointer seated to an array of N objects. */ +# define CHECK_N(ARG, N) _CHECK_N ((ARG), (N), 1) +/* Same as CHECK_N, but tolerate ARG == NULL. */ +# define CHECK_N_NULL_OK(ARG, N) _CHECK_N ((ARG), (N), __ptrvalue (ARG)) + +/* Check bounds of a pointer seated to a single object. */ +# define CHECK_1(ARG) CHECK_N ((ARG), 1) +/* Same as CHECK_1, but tolerate ARG == NULL. */ +# define CHECK_1_NULL_OK(ARG) CHECK_N_NULL_OK ((ARG), 1) + +/* Check for NUL-terminator within string's bounds. */ +# define CHECK_STRING(ARG) _CHECK_STRING ((ARG), 1) +/* Same as CHECK_STRING, but tolerate ARG == NULL. */ +# define CHECK_STRING_NULL_OK(ARG) _CHECK_STRING ((ARG), __ptrvalue (ARG)) + +/* Check bounds of signal syscall args with type sigset_t. */ +# define CHECK_SIGSET(SET) CHECK_N ((SET), _NSIG / (8 * sizeof *(SET))) +/* Same as CHECK_SIGSET, but tolerate SET == NULL. */ +# define CHECK_SIGSET_NULL_OK(SET) CHECK_N_NULL_OK ((SET), _NSIG / (8 * sizeof *(SET))) + +# if defined (_IOC_SIZESHIFT) && defined (_IOC_SIZEBITS) +/* Extract the size of the ioctl data and check its bounds. */ +# define CHECK_IOCTL(ARG, CMD) \ + CHECK_N ((const char *) (ARG), \ + (((CMD) >> _IOC_SIZESHIFT) & ((1 << _IOC_SIZEBITS) - 1))) +# else +/* We don't know the size of the ioctl data, so the best we can do + is check that the first byte is within bounds. */ +# define CHECK_IOCTL(ARG, CMD) CHECK_1 ((const char *) ARG) +# endif + +/* Check bounds of `struct flock *' for the locking fcntl commands. */ +# define CHECK_FCNTL(ARG, CMD) \ + (((CMD) == F_GETLK || (CMD) == F_SETLK || (CMD) == F_SETLKW) \ + ? CHECK_1 ((struct flock *) ARG) : (unsigned long) (ARG)) + +/* Check bounds of an array of mincore residency-status flags that + cover a region of NBYTES. Such a vector occupies one byte per page + of memory. */ +# define CHECK_N_PAGES(ARG, NBYTES) \ + ({ int _page_size_ = sysconf (_SC_PAGE_SIZE); \ + CHECK_N ((const char *) (ARG), \ + ((NBYTES) + _page_size_ - 1) / _page_size_); }) + +/* Return a bounded pointer with value PTR that satisfies CHECK_N (PTR, N). */ +# define BOUNDED_N(PTR, N) \ + ({ __typeof (PTR) __bounded _p_; \ + __ptrvalue _p_ = __ptrlow _p_ = __ptrvalue (PTR); \ + __ptrhigh _p_ = __ptrvalue _p_ + (N); \ + _p_; }) + +#else /* !__BOUNDED_POINTERS__ */ + +/* Do nothing if not compiling with -fbounded-pointers. */ + +# define BOUNDS_VIOLATED +# define CHECK_BOUNDS_LOW(ARG) (ARG) +# define CHECK_BOUNDS_HIGH(ARG) (ARG) +# define CHECK_1(ARG) (ARG) +# define CHECK_1_NULL_OK(ARG) (ARG) +# define CHECK_N(ARG, N) (ARG) +# define CHECK_N_NULL_OK(ARG, N) (ARG) +# define CHECK_STRING(ARG) (ARG) +# define CHECK_SIGSET(SET) (SET) +# define CHECK_SIGSET_NULL_OK(SET) (SET) +# define CHECK_IOCTL(ARG, CMD) (ARG) +# define CHECK_FCNTL(ARG, CMD) (ARG) +# define CHECK_N_PAGES(ARG, NBYTES) (ARG) +# define BOUNDED_N(PTR, N) (PTR) + +#endif /* !__BOUNDED_POINTERS__ */ + +#define BOUNDED_1(PTR) BOUNDED_N (PTR, 1) + +#endif /* _bp_checks_h_ */ diff --git a/libc/string/generic/memchr.c b/libc/string/generic/memchr.c new file mode 100644 index 000000000..6116b8f92 --- /dev/null +++ b/libc/string/generic/memchr.c @@ -0,0 +1,175 @@ +/* Copyright (C) 1991,93,96,97,99,2000,2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Based on strlen implementation by Torbjorn Granlund (tege@sics.se), + with help from Dan Sahlin (dan@sics.se) and + commentary by Jim Blandy (jimb@ai.mit.edu); + adaptation to memchr suggested by Dick Karpinski (dick@cca.ucsf.edu), + and implemented by Roland McGrath (roland@ai.mit.edu). + + 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. */ + +#include <string.h> +#include <stdlib.h> +#include <limits.h> + +#include "memcopy.h" + +#define LONG_MAX_32_BITS 2147483647 + +#undef memchr + +/* Search no more than N bytes of S for C. */ +void * memchr (const void * s, int c_in, size_t n) +{ + const unsigned char *char_ptr; + const unsigned long int *longword_ptr; + unsigned long int longword, magic_bits, charmask; + unsigned reg_char c; + + c = (unsigned char) c_in; + + /* Handle the first few characters by reading one character at a time. + Do this until CHAR_PTR is aligned on a longword boundary. */ + for (char_ptr = (const unsigned char *) s; + n > 0 && ((unsigned long int) char_ptr + & (sizeof (longword) - 1)) != 0; + --n, ++char_ptr) + if (*char_ptr == c) + return (void *) char_ptr; + + /* All these elucidatory comments refer to 4-byte longwords, + but the theory applies equally well to 8-byte longwords. */ + + longword_ptr = (unsigned long int *) char_ptr; + + /* Bits 31, 24, 16, and 8 of this number are zero. Call these bits + the "holes." Note that there is a hole just to the left of + each byte, with an extra at the end: + + bits: 01111110 11111110 11111110 11111111 + bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD + + The 1-bits make sure that carries propagate to the next 0-bit. + The 0-bits provide holes for carries to fall into. */ + + if (sizeof (longword) != 4 && sizeof (longword) != 8) + abort (); + +#if LONG_MAX <= LONG_MAX_32_BITS + magic_bits = 0x7efefeff; +#else + magic_bits = ((unsigned long int) 0x7efefefe << 32) | 0xfefefeff; +#endif + + /* Set up a longword, each of whose bytes is C. */ + charmask = c | (c << 8); + charmask |= charmask << 16; +#if LONG_MAX > LONG_MAX_32_BITS + charmask |= charmask << 32; +#endif + + /* Instead of the traditional loop which tests each character, + we will test a longword at a time. The tricky part is testing + if *any of the four* bytes in the longword in question are zero. */ + while (n >= sizeof (longword)) + { + /* We tentatively exit the loop if adding MAGIC_BITS to + LONGWORD fails to change any of the hole bits of LONGWORD. + + 1) Is this safe? Will it catch all the zero bytes? + Suppose there is a byte with all zeros. Any carry bits + propagating from its left will fall into the hole at its + least significant bit and stop. Since there will be no + carry from its most significant bit, the LSB of the + byte to the left will be unchanged, and the zero will be + detected. + + 2) Is this worthwhile? Will it ignore everything except + zero bytes? Suppose every byte of LONGWORD has a bit set + somewhere. There will be a carry into bit 8. If bit 8 + is set, this will carry into bit 16. If bit 8 is clear, + one of bits 9-15 must be set, so there will be a carry + into bit 16. Similarly, there will be a carry into bit + 24. If one of bits 24-30 is set, there will be a carry + into bit 31, so all of the hole bits will be changed. + + The one misfire occurs when bits 24-30 are clear and bit + 31 is set; in this case, the hole at bit 31 is not + changed. If we had access to the processor carry flag, + we could close this loophole by putting the fourth hole + at bit 32! + + So it ignores everything except 128's, when they're aligned + properly. + + 3) But wait! Aren't we looking for C, not zero? + Good point. So what we do is XOR LONGWORD with a longword, + each of whose bytes is C. This turns each byte that is C + into a zero. */ + + longword = *longword_ptr++ ^ charmask; + + /* Add MAGIC_BITS to LONGWORD. */ + if ((((longword + magic_bits) + + /* Set those bits that were unchanged by the addition. */ + ^ ~longword) + + /* Look at only the hole bits. If any of the hole bits + are unchanged, most likely one of the bytes was a + zero. */ + & ~magic_bits) != 0) + { + /* Which of the bytes was C? If none of them were, it was + a misfire; continue the search. */ + + const unsigned char *cp = (const unsigned char *) (longword_ptr - 1); + + if (cp[0] == c) + return (void *) cp; + if (cp[1] == c) + return (void *) &cp[1]; + if (cp[2] == c) + return (void *) &cp[2]; + if (cp[3] == c) + return (void *) &cp[3]; +#if LONG_MAX > 2147483647 + if (cp[4] == c) + return (void *) &cp[4]; + if (cp[5] == c) + return (void *) &cp[5]; + if (cp[6] == c) + return (void *) &cp[6]; + if (cp[7] == c) + return (void *) &cp[7]; +#endif + } + + n -= sizeof (longword); + } + + char_ptr = (const unsigned char *) longword_ptr; + + while (n-- > 0) + { + if (*char_ptr == c) + return (void *) char_ptr; + else + ++char_ptr; + } + + return 0; +} diff --git a/libc/string/generic/memcmp.c b/libc/string/generic/memcmp.c new file mode 100644 index 000000000..d9c3a7cab --- /dev/null +++ b/libc/string/generic/memcmp.c @@ -0,0 +1,340 @@ +/* Copyright (C) 1991,1993,1995,1997,1998,2003,2004 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Torbjorn Granlund (tege@sics.se). + + 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. */ + +#include <string.h> + +#undef memcmp + +#include "memcopy.h" + +#include <endian.h> + +#if __BYTE_ORDER == __BIG_ENDIAN +# define WORDS_BIGENDIAN +#endif + +#ifdef WORDS_BIGENDIAN +# define CMP_LT_OR_GT(a, b) ((a) > (b) ? 1 : -1) +#else +# define CMP_LT_OR_GT(a, b) memcmp_bytes ((a), (b)) +#endif + +/* BE VERY CAREFUL IF YOU CHANGE THIS CODE! */ + +/* The strategy of this memcmp is: + + 1. Compare bytes until one of the block pointers is aligned. + + 2. Compare using memcmp_common_alignment or + memcmp_not_common_alignment, regarding the alignment of the other + block after the initial byte operations. The maximum number of + full words (of type op_t) are compared in this way. + + 3. Compare the few remaining bytes. */ + +#ifndef WORDS_BIGENDIAN +/* memcmp_bytes -- Compare A and B bytewise in the byte order of the machine. + A and B are known to be different. + This is needed only on little-endian machines. */ + +static int memcmp_bytes __P((op_t, op_t)); + +# ifdef __GNUC__ +__inline +# endif +static int +memcmp_bytes (a, b) + op_t a, b; +{ + long int srcp1 = (long int) &a; + long int srcp2 = (long int) &b; + op_t a0, b0; + + do + { + a0 = ((byte *) srcp1)[0]; + b0 = ((byte *) srcp2)[0]; + srcp1 += 1; + srcp2 += 1; + } + while (a0 == b0); + return a0 - b0; +} +#endif + +static int memcmp_common_alignment __P((long, long, size_t)); + +/* memcmp_common_alignment -- Compare blocks at SRCP1 and SRCP2 with LEN `op_t' + objects (not LEN bytes!). Both SRCP1 and SRCP2 should be aligned for + memory operations on `op_t's. */ +static int +memcmp_common_alignment (srcp1, srcp2, len) + long int srcp1; + long int srcp2; + size_t len; +{ + op_t a0, a1; + op_t b0, b1; + + switch (len % 4) + { + default: /* Avoid warning about uninitialized local variables. */ + case 2: + a0 = ((op_t *) srcp1)[0]; + b0 = ((op_t *) srcp2)[0]; + srcp1 -= 2 * OPSIZ; + srcp2 -= 2 * OPSIZ; + len += 2; + goto do1; + case 3: + a1 = ((op_t *) srcp1)[0]; + b1 = ((op_t *) srcp2)[0]; + srcp1 -= OPSIZ; + srcp2 -= OPSIZ; + len += 1; + goto do2; + case 0: + if (OP_T_THRES <= 3 * OPSIZ && len == 0) + return 0; + a0 = ((op_t *) srcp1)[0]; + b0 = ((op_t *) srcp2)[0]; + goto do3; + case 1: + a1 = ((op_t *) srcp1)[0]; + b1 = ((op_t *) srcp2)[0]; + srcp1 += OPSIZ; + srcp2 += OPSIZ; + len -= 1; + if (OP_T_THRES <= 3 * OPSIZ && len == 0) + goto do0; + /* Fall through. */ + } + + do + { + a0 = ((op_t *) srcp1)[0]; + b0 = ((op_t *) srcp2)[0]; + if (a1 != b1) + return CMP_LT_OR_GT (a1, b1); + + do3: + a1 = ((op_t *) srcp1)[1]; + b1 = ((op_t *) srcp2)[1]; + if (a0 != b0) + return CMP_LT_OR_GT (a0, b0); + + do2: + a0 = ((op_t *) srcp1)[2]; + b0 = ((op_t *) srcp2)[2]; + if (a1 != b1) + return CMP_LT_OR_GT (a1, b1); + + do1: + a1 = ((op_t *) srcp1)[3]; + b1 = ((op_t *) srcp2)[3]; + if (a0 != b0) + return CMP_LT_OR_GT (a0, b0); + + srcp1 += 4 * OPSIZ; + srcp2 += 4 * OPSIZ; + len -= 4; + } + while (len != 0); + + /* This is the right position for do0. Please don't move + it into the loop. */ + do0: + if (a1 != b1) + return CMP_LT_OR_GT (a1, b1); + return 0; +} + +static int memcmp_not_common_alignment __P((long, long, size_t)); + +/* memcmp_not_common_alignment -- Compare blocks at SRCP1 and SRCP2 with LEN + `op_t' objects (not LEN bytes!). SRCP2 should be aligned for memory + operations on `op_t', but SRCP1 *should be unaligned*. */ +static int +memcmp_not_common_alignment (srcp1, srcp2, len) + long int srcp1; + long int srcp2; + size_t len; +{ + op_t a0, a1, a2, a3; + op_t b0, b1, b2, b3; + op_t x; + int shl, shr; + + /* Calculate how to shift a word read at the memory operation + aligned srcp1 to make it aligned for comparison. */ + + shl = 8 * (srcp1 % OPSIZ); + shr = 8 * OPSIZ - shl; + + /* Make SRCP1 aligned by rounding it down to the beginning of the `op_t' + it points in the middle of. */ + srcp1 &= -OPSIZ; + + switch (len % 4) + { + default: /* Avoid warning about uninitialized local variables. */ + case 2: + a1 = ((op_t *) srcp1)[0]; + a2 = ((op_t *) srcp1)[1]; + b2 = ((op_t *) srcp2)[0]; + srcp1 -= 1 * OPSIZ; + srcp2 -= 2 * OPSIZ; + len += 2; + goto do1; + case 3: + a0 = ((op_t *) srcp1)[0]; + a1 = ((op_t *) srcp1)[1]; + b1 = ((op_t *) srcp2)[0]; + srcp2 -= 1 * OPSIZ; + len += 1; + goto do2; + case 0: + if (OP_T_THRES <= 3 * OPSIZ && len == 0) + return 0; + a3 = ((op_t *) srcp1)[0]; + a0 = ((op_t *) srcp1)[1]; + b0 = ((op_t *) srcp2)[0]; + srcp1 += 1 * OPSIZ; + goto do3; + case 1: + a2 = ((op_t *) srcp1)[0]; + a3 = ((op_t *) srcp1)[1]; + b3 = ((op_t *) srcp2)[0]; + srcp1 += 2 * OPSIZ; + srcp2 += 1 * OPSIZ; + len -= 1; + if (OP_T_THRES <= 3 * OPSIZ && len == 0) + goto do0; + /* Fall through. */ + } + + do + { + a0 = ((op_t *) srcp1)[0]; + b0 = ((op_t *) srcp2)[0]; + x = MERGE(a2, shl, a3, shr); + if (x != b3) + return CMP_LT_OR_GT (x, b3); + + do3: + a1 = ((op_t *) srcp1)[1]; + b1 = ((op_t *) srcp2)[1]; + x = MERGE(a3, shl, a0, shr); + if (x != b0) + return CMP_LT_OR_GT (x, b0); + + do2: + a2 = ((op_t *) srcp1)[2]; + b2 = ((op_t *) srcp2)[2]; + x = MERGE(a0, shl, a1, shr); + if (x != b1) + return CMP_LT_OR_GT (x, b1); + + do1: + a3 = ((op_t *) srcp1)[3]; + b3 = ((op_t *) srcp2)[3]; + x = MERGE(a1, shl, a2, shr); + if (x != b2) + return CMP_LT_OR_GT (x, b2); + + srcp1 += 4 * OPSIZ; + srcp2 += 4 * OPSIZ; + len -= 4; + } + while (len != 0); + + /* This is the right position for do0. Please don't move + it into the loop. */ + do0: + x = MERGE(a2, shl, a3, shr); + if (x != b3) + return CMP_LT_OR_GT (x, b3); + return 0; +} + +int +memcmp (s1, s2, len) + const __ptr_t s1; + const __ptr_t s2; + size_t len; +{ + op_t a0; + op_t b0; + long int srcp1 = (long int) s1; + long int srcp2 = (long int) s2; + op_t res; + + if (len >= OP_T_THRES) + { + /* There are at least some bytes to compare. No need to test + for LEN == 0 in this alignment loop. */ + while (srcp2 % OPSIZ != 0) + { + a0 = ((byte *) srcp1)[0]; + b0 = ((byte *) srcp2)[0]; + srcp1 += 1; + srcp2 += 1; + res = a0 - b0; + if (res != 0) + return res; + len -= 1; + } + + /* SRCP2 is now aligned for memory operations on `op_t'. + SRCP1 alignment determines if we can do a simple, + aligned compare or need to shuffle bits. */ + + if (srcp1 % OPSIZ == 0) + res = memcmp_common_alignment (srcp1, srcp2, len / OPSIZ); + else + res = memcmp_not_common_alignment (srcp1, srcp2, len / OPSIZ); + if (res != 0) + return res; + + /* Number of bytes remaining in the interval [0..OPSIZ-1]. */ + srcp1 += len & -OPSIZ; + srcp2 += len & -OPSIZ; + len %= OPSIZ; + } + + /* There are just a few bytes to compare. Use byte memory operations. */ + while (len != 0) + { + a0 = ((byte *) srcp1)[0]; + b0 = ((byte *) srcp2)[0]; + srcp1 += 1; + srcp2 += 1; + res = a0 - b0; + if (res != 0) + return res; + len -= 1; + } + + return 0; +} +#ifdef weak_alias +# undef bcmp +weak_alias (memcmp, bcmp) +#endif diff --git a/libc/string/generic/memcopy.h b/libc/string/generic/memcopy.h new file mode 100644 index 000000000..df1ba9a97 --- /dev/null +++ b/libc/string/generic/memcopy.h @@ -0,0 +1,150 @@ +/* memcopy.h -- definitions for memory copy functions. Generic C version. + Copyright (C) 1991, 1992, 1993, 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Torbjorn Granlund (tege@sics.se). + + 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. */ + +/* The strategy of the memory functions is: + + 1. Copy bytes until the destination pointer is aligned. + + 2. Copy words in unrolled loops. If the source and destination + are not aligned in the same way, use word memory operations, + but shift and merge two read words before writing. + + 3. Copy the few remaining bytes. + + This is fast on processors that have at least 10 registers for + allocation by GCC, and that can access memory at reg+const in one + instruction. + + I made an "exhaustive" test of this memmove when I wrote it, + exhaustive in the sense that I tried all alignment and length + combinations, with and without overlap. */ + +#include <sys/cdefs.h> +#include <endian.h> + +/* The macros defined in this file are: + + BYTE_COPY_FWD(dst_beg_ptr, src_beg_ptr, nbytes_to_copy) + + BYTE_COPY_BWD(dst_end_ptr, src_end_ptr, nbytes_to_copy) + + WORD_COPY_FWD(dst_beg_ptr, src_beg_ptr, nbytes_remaining, nbytes_to_copy) + + WORD_COPY_BWD(dst_end_ptr, src_end_ptr, nbytes_remaining, nbytes_to_copy) + + MERGE(old_word, sh_1, new_word, sh_2) + [I fail to understand. I feel stupid. --roland] +*/ + +/* Type to use for aligned memory operations. + This should normally be the biggest type supported by a single load + and store. */ +#define op_t unsigned long int +#define OPSIZ (sizeof(op_t)) + +/* Type to use for unaligned operations. */ +typedef unsigned char byte; + +/* Optimal type for storing bytes in registers. */ +#define reg_char char + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define MERGE(w0, sh_1, w1, sh_2) (((w0) >> (sh_1)) | ((w1) << (sh_2))) +#endif +#if __BYTE_ORDER == __BIG_ENDIAN +#define MERGE(w0, sh_1, w1, sh_2) (((w0) << (sh_1)) | ((w1) >> (sh_2))) +#endif + +/* Copy exactly NBYTES bytes from SRC_BP to DST_BP, + without any assumptions about alignment of the pointers. */ +#define BYTE_COPY_FWD(dst_bp, src_bp, nbytes) \ + do \ + { \ + size_t __nbytes = (nbytes); \ + while (__nbytes > 0) \ + { \ + byte __x = ((byte *) src_bp)[0]; \ + src_bp += 1; \ + __nbytes -= 1; \ + ((byte *) dst_bp)[0] = __x; \ + dst_bp += 1; \ + } \ + } while (0) + +/* Copy exactly NBYTES_TO_COPY bytes from SRC_END_PTR to DST_END_PTR, + beginning at the bytes right before the pointers and continuing towards + smaller addresses. Don't assume anything about alignment of the + pointers. */ +#define BYTE_COPY_BWD(dst_ep, src_ep, nbytes) \ + do \ + { \ + size_t __nbytes = (nbytes); \ + while (__nbytes > 0) \ + { \ + byte __x; \ + src_ep -= 1; \ + __x = ((byte *) src_ep)[0]; \ + dst_ep -= 1; \ + __nbytes -= 1; \ + ((byte *) dst_ep)[0] = __x; \ + } \ + } while (0) + +/* Copy *up to* NBYTES bytes from SRC_BP to DST_BP, with + the assumption that DST_BP is aligned on an OPSIZ multiple. If + not all bytes could be easily copied, store remaining number of bytes + in NBYTES_LEFT, otherwise store 0. */ +/* extern void _wordcopy_fwd_aligned __P ((long int, long int, size_t)); */ +/* extern void _wordcopy_fwd_dest_aligned __P ((long int, long int, size_t)); */ +#define WORD_COPY_FWD(dst_bp, src_bp, nbytes_left, nbytes) \ + do \ + { \ + if (src_bp % OPSIZ == 0) \ + _wordcopy_fwd_aligned (dst_bp, src_bp, (nbytes) / OPSIZ); \ + else \ + _wordcopy_fwd_dest_aligned (dst_bp, src_bp, (nbytes) / OPSIZ); \ + src_bp += (nbytes) & -OPSIZ; \ + dst_bp += (nbytes) & -OPSIZ; \ + (nbytes_left) = (nbytes) % OPSIZ; \ + } while (0) + +/* Copy *up to* NBYTES_TO_COPY bytes from SRC_END_PTR to DST_END_PTR, + beginning at the words (of type op_t) right before the pointers and + continuing towards smaller addresses. May take advantage of that + DST_END_PTR is aligned on an OPSIZ multiple. If not all bytes could be + easily copied, store remaining number of bytes in NBYTES_REMAINING, + otherwise store 0. */ +/* extern void _wordcopy_bwd_aligned __P ((long int, long int, size_t)); */ +/* extern void _wordcopy_bwd_dest_aligned __P ((long int, long int, size_t)); */ +#define WORD_COPY_BWD(dst_ep, src_ep, nbytes_left, nbytes) \ + do \ + { \ + if (src_ep % OPSIZ == 0) \ + _wordcopy_bwd_aligned (dst_ep, src_ep, (nbytes) / OPSIZ); \ + else \ + _wordcopy_bwd_dest_aligned (dst_ep, src_ep, (nbytes) / OPSIZ); \ + src_ep -= (nbytes) & -OPSIZ; \ + dst_ep -= (nbytes) & -OPSIZ; \ + (nbytes_left) = (nbytes) % OPSIZ; \ + } while (0) + + +/* Threshold value for when to enter the unrolled loops. */ +#define OP_T_THRES 16 diff --git a/libc/string/generic/memcpy.c b/libc/string/generic/memcpy.c new file mode 100644 index 000000000..9851fdea6 --- /dev/null +++ b/libc/string/generic/memcpy.c @@ -0,0 +1,246 @@ +/* Copy memory to memory until the specified number of bytes + has been copied. Overlap is NOT handled correctly. + Copyright (C) 1991, 1997, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Torbjorn Granlund (tege@sics.se). + + 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. */ + +#include <string.h> +#include "memcopy.h" +#include "pagecopy.h" + +#undef memcpy + +/* _wordcopy_fwd_aligned -- Copy block beginning at SRCP to + block beginning at DSTP with LEN `op_t' words (not LEN bytes!). + Both SRCP and DSTP should be aligned for memory operations on `op_t's. */ + +static void _wordcopy_fwd_aligned (long int dstp, long int srcp, size_t len) +{ + op_t a0, a1; + + switch (len % 8) + { + case 2: + a0 = ((op_t *) srcp)[0]; + srcp -= 6 * OPSIZ; + dstp -= 7 * OPSIZ; + len += 6; + goto do1; + case 3: + a1 = ((op_t *) srcp)[0]; + srcp -= 5 * OPSIZ; + dstp -= 6 * OPSIZ; + len += 5; + goto do2; + case 4: + a0 = ((op_t *) srcp)[0]; + srcp -= 4 * OPSIZ; + dstp -= 5 * OPSIZ; + len += 4; + goto do3; + case 5: + a1 = ((op_t *) srcp)[0]; + srcp -= 3 * OPSIZ; + dstp -= 4 * OPSIZ; + len += 3; + goto do4; + case 6: + a0 = ((op_t *) srcp)[0]; + srcp -= 2 * OPSIZ; + dstp -= 3 * OPSIZ; + len += 2; + goto do5; + case 7: + a1 = ((op_t *) srcp)[0]; + srcp -= 1 * OPSIZ; + dstp -= 2 * OPSIZ; + len += 1; + goto do6; + + case 0: + if (OP_T_THRES <= 3 * OPSIZ && len == 0) + return; + a0 = ((op_t *) srcp)[0]; + srcp -= 0 * OPSIZ; + dstp -= 1 * OPSIZ; + goto do7; + case 1: + a1 = ((op_t *) srcp)[0]; + srcp -=-1 * OPSIZ; + dstp -= 0 * OPSIZ; + len -= 1; + if (OP_T_THRES <= 3 * OPSIZ && len == 0) + goto do0; + goto do8; /* No-op. */ + } + + do + { + do8: + a0 = ((op_t *) srcp)[0]; + ((op_t *) dstp)[0] = a1; + do7: + a1 = ((op_t *) srcp)[1]; + ((op_t *) dstp)[1] = a0; + do6: + a0 = ((op_t *) srcp)[2]; + ((op_t *) dstp)[2] = a1; + do5: + a1 = ((op_t *) srcp)[3]; + ((op_t *) dstp)[3] = a0; + do4: + a0 = ((op_t *) srcp)[4]; + ((op_t *) dstp)[4] = a1; + do3: + a1 = ((op_t *) srcp)[5]; + ((op_t *) dstp)[5] = a0; + do2: + a0 = ((op_t *) srcp)[6]; + ((op_t *) dstp)[6] = a1; + do1: + a1 = ((op_t *) srcp)[7]; + ((op_t *) dstp)[7] = a0; + + srcp += 8 * OPSIZ; + dstp += 8 * OPSIZ; + len -= 8; + } + while (len != 0); + + /* This is the right position for do0. Please don't move + it into the loop. */ + do0: + ((op_t *) dstp)[0] = a1; +} + +/* _wordcopy_fwd_dest_aligned -- Copy block beginning at SRCP to + block beginning at DSTP with LEN `op_t' words (not LEN bytes!). + DSTP should be aligned for memory operations on `op_t's, but SRCP must + *not* be aligned. */ + +static void _wordcopy_fwd_dest_aligned (long int dstp, long int srcp, size_t len) +{ + op_t a0, a1, a2, a3; + int sh_1, sh_2; + + /* Calculate how to shift a word read at the memory operation + aligned srcp to make it aligned for copy. */ + + sh_1 = 8 * (srcp % OPSIZ); + sh_2 = 8 * OPSIZ - sh_1; + + /* Make SRCP aligned by rounding it down to the beginning of the `op_t' + it points in the middle of. */ + srcp &= -OPSIZ; + + switch (len % 4) + { + case 2: + a1 = ((op_t *) srcp)[0]; + a2 = ((op_t *) srcp)[1]; + srcp -= 1 * OPSIZ; + dstp -= 3 * OPSIZ; + len += 2; + goto do1; + case 3: + a0 = ((op_t *) srcp)[0]; + a1 = ((op_t *) srcp)[1]; + srcp -= 0 * OPSIZ; + dstp -= 2 * OPSIZ; + len += 1; + goto do2; + case 0: + if (OP_T_THRES <= 3 * OPSIZ && len == 0) + return; + a3 = ((op_t *) srcp)[0]; + a0 = ((op_t *) srcp)[1]; + srcp -=-1 * OPSIZ; + dstp -= 1 * OPSIZ; + len += 0; + goto do3; + case 1: + a2 = ((op_t *) srcp)[0]; + a3 = ((op_t *) srcp)[1]; + srcp -=-2 * OPSIZ; + dstp -= 0 * OPSIZ; + len -= 1; + if (OP_T_THRES <= 3 * OPSIZ && len == 0) + goto do0; + goto do4; /* No-op. */ + } + + do + { + do4: + a0 = ((op_t *) srcp)[0]; + ((op_t *) dstp)[0] = MERGE (a2, sh_1, a3, sh_2); + do3: + a1 = ((op_t *) srcp)[1]; + ((op_t *) dstp)[1] = MERGE (a3, sh_1, a0, sh_2); + do2: + a2 = ((op_t *) srcp)[2]; + ((op_t *) dstp)[2] = MERGE (a0, sh_1, a1, sh_2); + do1: + a3 = ((op_t *) srcp)[3]; + ((op_t *) dstp)[3] = MERGE (a1, sh_1, a2, sh_2); + + srcp += 4 * OPSIZ; + dstp += 4 * OPSIZ; + len -= 4; + } + while (len != 0); + + /* This is the right position for do0. Please don't move + it into the loop. */ + do0: + ((op_t *) dstp)[0] = MERGE (a2, sh_1, a3, sh_2); +} + +void *memcpy (void *dstpp, const void *srcpp, size_t len) +{ + unsigned long int dstp = (long int) dstpp; + unsigned long int srcp = (long int) srcpp; + + /* Copy from the beginning to the end. */ + + /* If there not too few bytes to copy, use word copy. */ + if (len >= OP_T_THRES) + { + /* Copy just a few bytes to make DSTP aligned. */ + len -= (-dstp) % OPSIZ; + BYTE_COPY_FWD (dstp, srcp, (-dstp) % OPSIZ); + + /* Copy whole pages from SRCP to DSTP by virtual address manipulation, + as much as possible. */ + + PAGE_COPY_FWD_MAYBE (dstp, srcp, len, len); + + /* Copy from SRCP to DSTP taking advantage of the known alignment of + DSTP. Number of bytes remaining is put in the third argument, + i.e. in LEN. This number may vary from machine to machine. */ + + WORD_COPY_FWD (dstp, srcp, len, len); + + /* Fall out and copy the tail. */ + } + + /* There are just a few bytes to copy. Use byte memory operations. */ + BYTE_COPY_FWD (dstp, srcp, len); + + return dstpp; +} diff --git a/libc/string/generic/memmem.c b/libc/string/generic/memmem.c new file mode 100644 index 000000000..c0f2bd192 --- /dev/null +++ b/libc/string/generic/memmem.c @@ -0,0 +1,50 @@ +/* Copyright (C) 1991,92,93,94,96,97,98,2000 Free Software Foundation, Inc. + 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. */ + +#include <string.h> +#include <stddef.h> + +#undef memmem + +/* Return the first occurrence of NEEDLE in HAYSTACK. */ +void *memmem (const void *haystack, size_t haystack_len, + const void *needle, size_t needle_len) +{ + const char *begin; + const char *const last_possible + = (const char *) haystack + haystack_len - needle_len; + + if (needle_len == 0) + /* The first occurrence of the empty string is deemed to occur at + the beginning of the string. */ + return (void *) haystack; + + /* Sanity check, otherwise the loop might search through the whole + memory. */ + if (__builtin_expect (haystack_len < needle_len, 0)) + return NULL; + + for (begin = (const char *) haystack; begin <= last_possible; ++begin) + if (begin[0] == ((const char *) needle)[0] && + !memcmp ((const void *) &begin[1], + (const void *) ((const char *) needle + 1), + needle_len - 1)) + return (void *) begin; + + return NULL; +} diff --git a/libc/string/generic/memmove.c b/libc/string/generic/memmove.c new file mode 100644 index 000000000..21876ce6e --- /dev/null +++ b/libc/string/generic/memmove.c @@ -0,0 +1,278 @@ +/* Copy memory to memory until the specified number of bytes + has been copied. Overlap is handled correctly. + Copyright (C) 1991, 1995, 1996, 1997, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Torbjorn Granlund (tege@sics.se). + + 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. */ + +#include <string.h> + +#include "memcopy.h" +#include "pagecopy.h" + +static void _wordcopy_bwd_aligned (long int dstp, long int srcp, size_t len) +{ + op_t a0, a1; + + switch (len % 8) + { + case 2: + srcp -= 2 * OPSIZ; + dstp -= 1 * OPSIZ; + a0 = ((op_t *) srcp)[1]; + len += 6; + goto do1; + case 3: + srcp -= 3 * OPSIZ; + dstp -= 2 * OPSIZ; + a1 = ((op_t *) srcp)[2]; + len += 5; + goto do2; + case 4: + srcp -= 4 * OPSIZ; + dstp -= 3 * OPSIZ; + a0 = ((op_t *) srcp)[3]; + len += 4; + goto do3; + case 5: + srcp -= 5 * OPSIZ; + dstp -= 4 * OPSIZ; + a1 = ((op_t *) srcp)[4]; + len += 3; + goto do4; + case 6: + srcp -= 6 * OPSIZ; + dstp -= 5 * OPSIZ; + a0 = ((op_t *) srcp)[5]; + len += 2; + goto do5; + case 7: + srcp -= 7 * OPSIZ; + dstp -= 6 * OPSIZ; + a1 = ((op_t *) srcp)[6]; + len += 1; + goto do6; + + case 0: + if (OP_T_THRES <= 3 * OPSIZ && len == 0) + return; + srcp -= 8 * OPSIZ; + dstp -= 7 * OPSIZ; + a0 = ((op_t *) srcp)[7]; + goto do7; + case 1: + srcp -= 9 * OPSIZ; + dstp -= 8 * OPSIZ; + a1 = ((op_t *) srcp)[8]; + len -= 1; + if (OP_T_THRES <= 3 * OPSIZ && len == 0) + goto do0; + goto do8; /* No-op. */ + } + + do + { + do8: + a0 = ((op_t *) srcp)[7]; + ((op_t *) dstp)[7] = a1; + do7: + a1 = ((op_t *) srcp)[6]; + ((op_t *) dstp)[6] = a0; + do6: + a0 = ((op_t *) srcp)[5]; + ((op_t *) dstp)[5] = a1; + do5: + a1 = ((op_t *) srcp)[4]; + ((op_t *) dstp)[4] = a0; + do4: + a0 = ((op_t *) srcp)[3]; + ((op_t *) dstp)[3] = a1; + do3: + a1 = ((op_t *) srcp)[2]; + ((op_t *) dstp)[2] = a0; + do2: + a0 = ((op_t *) srcp)[1]; + ((op_t *) dstp)[1] = a1; + do1: + a1 = ((op_t *) srcp)[0]; + ((op_t *) dstp)[0] = a0; + + srcp -= 8 * OPSIZ; + dstp -= 8 * OPSIZ; + len -= 8; + } + while (len != 0); + + /* This is the right position for do0. Please don't move + it into the loop. */ + do0: + ((op_t *) dstp)[7] = a1; +} + +/* _wordcopy_bwd_dest_aligned -- Copy block finishing right + before SRCP to block finishing right before DSTP with LEN `op_t' + words (not LEN bytes!). DSTP should be aligned for memory + operations on `op_t', but SRCP must *not* be aligned. */ + +static void _wordcopy_bwd_dest_aligned (long int dstp, long int srcp, size_t len) +{ + op_t a0, a1, a2, a3; + int sh_1, sh_2; + + /* Calculate how to shift a word read at the memory operation + aligned srcp to make it aligned for copy. */ + + sh_1 = 8 * (srcp % OPSIZ); + sh_2 = 8 * OPSIZ - sh_1; + + /* Make srcp aligned by rounding it down to the beginning of the op_t + it points in the middle of. */ + srcp &= -OPSIZ; + srcp += OPSIZ; + + switch (len % 4) + { + case 2: + srcp -= 3 * OPSIZ; + dstp -= 1 * OPSIZ; + a2 = ((op_t *) srcp)[2]; + a1 = ((op_t *) srcp)[1]; + len += 2; + goto do1; + case 3: + srcp -= 4 * OPSIZ; + dstp -= 2 * OPSIZ; + a3 = ((op_t *) srcp)[3]; + a2 = ((op_t *) srcp)[2]; + len += 1; + goto do2; + case 0: + if (OP_T_THRES <= 3 * OPSIZ && len == 0) + return; + srcp -= 5 * OPSIZ; + dstp -= 3 * OPSIZ; + a0 = ((op_t *) srcp)[4]; + a3 = ((op_t *) srcp)[3]; + goto do3; + case 1: + srcp -= 6 * OPSIZ; + dstp -= 4 * OPSIZ; + a1 = ((op_t *) srcp)[5]; + a0 = ((op_t *) srcp)[4]; + len -= 1; + if (OP_T_THRES <= 3 * OPSIZ && len == 0) + goto do0; + goto do4; /* No-op. */ + } + + do + { + do4: + a3 = ((op_t *) srcp)[3]; + ((op_t *) dstp)[3] = MERGE (a0, sh_1, a1, sh_2); + do3: + a2 = ((op_t *) srcp)[2]; + ((op_t *) dstp)[2] = MERGE (a3, sh_1, a0, sh_2); + do2: + a1 = ((op_t *) srcp)[1]; + ((op_t *) dstp)[1] = MERGE (a2, sh_1, a3, sh_2); + do1: + a0 = ((op_t *) srcp)[0]; + ((op_t *) dstp)[0] = MERGE (a1, sh_1, a2, sh_2); + + srcp -= 4 * OPSIZ; + dstp -= 4 * OPSIZ; + len -= 4; + } + while (len != 0); + + /* This is the right position for do0. Please don't move + it into the loop. */ + do0: + ((op_t *) dstp)[3] = MERGE (a0, sh_1, a1, sh_2); +} + +void *memmove (void *dest, const void *src, size_t len) +{ + unsigned long int dstp = (long int) dest; + unsigned long int srcp = (long int) src; + + /* This test makes the forward copying code be used whenever possible. + Reduces the working set. */ + if (dstp - srcp >= len) /* *Unsigned* compare! */ + { +#if 1 +#warning REMINDER: generic-opt memmove assumes memcpy does forward copying! + memcpy(dest, src, len); +#else + /* Copy from the beginning to the end. */ + + /* If there not too few bytes to copy, use word copy. */ + if (len >= OP_T_THRES) + { + /* Copy just a few bytes to make DSTP aligned. */ + len -= (-dstp) % OPSIZ; + BYTE_COPY_FWD (dstp, srcp, (-dstp) % OPSIZ); + + /* Copy whole pages from SRCP to DSTP by virtual address + manipulation, as much as possible. */ + + PAGE_COPY_FWD_MAYBE (dstp, srcp, len, len); + + /* Copy from SRCP to DSTP taking advantage of the known + alignment of DSTP. Number of bytes remaining is put + in the third argument, i.e. in LEN. This number may + vary from machine to machine. */ + + WORD_COPY_FWD (dstp, srcp, len, len); + + /* Fall out and copy the tail. */ + } + + /* There are just a few bytes to copy. Use byte memory operations. */ + BYTE_COPY_FWD (dstp, srcp, len); +#endif + } + else + { + /* Copy from the end to the beginning. */ + srcp += len; + dstp += len; + + /* If there not too few bytes to copy, use word copy. */ + if (len >= OP_T_THRES) + { + /* Copy just a few bytes to make DSTP aligned. */ + len -= dstp % OPSIZ; + BYTE_COPY_BWD (dstp, srcp, dstp % OPSIZ); + + /* Copy from SRCP to DSTP taking advantage of the known + alignment of DSTP. Number of bytes remaining is put + in the third argument, i.e. in LEN. This number may + vary from machine to machine. */ + + WORD_COPY_BWD (dstp, srcp, len, len); + + /* Fall out and copy the tail. */ + } + + /* There are just a few bytes to copy. Use byte memory operations. */ + BYTE_COPY_BWD (dstp, srcp, len); + } + + return (dest); +} diff --git a/libc/string/generic/mempcpy.c b/libc/string/generic/mempcpy.c new file mode 100644 index 000000000..46a2eaf92 --- /dev/null +++ b/libc/string/generic/mempcpy.c @@ -0,0 +1,18 @@ +/* Copy memory to memory until the specified number of bytes + has been copied, return pointer to following byte. + Overlap is NOT handled correctly. +*/ + +/* Ditch the glibc version and just wrap memcpy. */ + +#include <string.h> + +#undef mempcpy +#undef __mempcpy + +void *__mempcpy (void *dstpp, const void *srcpp, size_t len) +{ + memcpy(dstpp, srcpp, len); + return (void *)(((char *)dstpp) + len); +} +weak_alias (__mempcpy, mempcpy) diff --git a/libc/string/generic/memrchr.c b/libc/string/generic/memrchr.c new file mode 100644 index 000000000..f4d435e09 --- /dev/null +++ b/libc/string/generic/memrchr.c @@ -0,0 +1,174 @@ +/* memrchr -- find the last occurrence of a byte in a memory block + Copyright (C) 1991, 93, 96, 97, 99, 2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Based on strlen implementation by Torbjorn Granlund (tege@sics.se), + with help from Dan Sahlin (dan@sics.se) and + commentary by Jim Blandy (jimb@ai.mit.edu); + adaptation to memchr suggested by Dick Karpinski (dick@cca.ucsf.edu), + and implemented by Roland McGrath (roland@ai.mit.edu). + + 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. */ + +#include <string.h> +#include <stdlib.h> +#include <limits.h> + +#include "memcopy.h" + +#define LONG_MAX_32_BITS 2147483647 + +#undef memrchr + +/* Search no more than N bytes of S for C. */ +void *memrchr (const void * s, int c_in, size_t n) +{ + const unsigned char *char_ptr; + const unsigned long int *longword_ptr; + unsigned long int longword, magic_bits, charmask; + unsigned reg_char c; + + c = (unsigned char) c_in; + + /* Handle the last few characters by reading one character at a time. + Do this until CHAR_PTR is aligned on a longword boundary. */ + for (char_ptr = (const unsigned char *) s + n; + n > 0 && ((unsigned long int) char_ptr + & (sizeof (longword) - 1)) != 0; + --n) + if (*--char_ptr == c) + return (void *) char_ptr; + + /* All these elucidatory comments refer to 4-byte longwords, + but the theory applies equally well to 8-byte longwords. */ + + longword_ptr = (const unsigned long int *) char_ptr; + + /* Bits 31, 24, 16, and 8 of this number are zero. Call these bits + the "holes." Note that there is a hole just to the left of + each byte, with an extra at the end: + + bits: 01111110 11111110 11111110 11111111 + bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD + + The 1-bits make sure that carries propagate to the next 0-bit. + The 0-bits provide holes for carries to fall into. */ + + if (sizeof (longword) != 4 && sizeof (longword) != 8) + abort (); + +#if LONG_MAX <= LONG_MAX_32_BITS + magic_bits = 0x7efefeff; +#else + magic_bits = ((unsigned long int) 0x7efefefe << 32) | 0xfefefeff; +#endif + + /* Set up a longword, each of whose bytes is C. */ + charmask = c | (c << 8); + charmask |= charmask << 16; +#if LONG_MAX > LONG_MAX_32_BITS + charmask |= charmask << 32; +#endif + + /* Instead of the traditional loop which tests each character, + we will test a longword at a time. The tricky part is testing + if *any of the four* bytes in the longword in question are zero. */ + while (n >= sizeof (longword)) + { + /* We tentatively exit the loop if adding MAGIC_BITS to + LONGWORD fails to change any of the hole bits of LONGWORD. + + 1) Is this safe? Will it catch all the zero bytes? + Suppose there is a byte with all zeros. Any carry bits + propagating from its left will fall into the hole at its + least significant bit and stop. Since there will be no + carry from its most significant bit, the LSB of the + byte to the left will be unchanged, and the zero will be + detected. + + 2) Is this worthwhile? Will it ignore everything except + zero bytes? Suppose every byte of LONGWORD has a bit set + somewhere. There will be a carry into bit 8. If bit 8 + is set, this will carry into bit 16. If bit 8 is clear, + one of bits 9-15 must be set, so there will be a carry + into bit 16. Similarly, there will be a carry into bit + 24. If one of bits 24-30 is set, there will be a carry + into bit 31, so all of the hole bits will be changed. + + The one misfire occurs when bits 24-30 are clear and bit + 31 is set; in this case, the hole at bit 31 is not + changed. If we had access to the processor carry flag, + we could close this loophole by putting the fourth hole + at bit 32! + + So it ignores everything except 128's, when they're aligned + properly. + + 3) But wait! Aren't we looking for C, not zero? + Good point. So what we do is XOR LONGWORD with a longword, + each of whose bytes is C. This turns each byte that is C + into a zero. */ + + longword = *--longword_ptr ^ charmask; + + /* Add MAGIC_BITS to LONGWORD. */ + if ((((longword + magic_bits) + + /* Set those bits that were unchanged by the addition. */ + ^ ~longword) + + /* Look at only the hole bits. If any of the hole bits + are unchanged, most likely one of the bytes was a + zero. */ + & ~magic_bits) != 0) + { + /* Which of the bytes was C? If none of them were, it was + a misfire; continue the search. */ + + const unsigned char *cp = (const unsigned char *) longword_ptr; + +#if LONG_MAX > 2147483647 + if (cp[7] == c) + return (void *) &cp[7]; + if (cp[6] == c) + return (void *) &cp[6]; + if (cp[5] == c) + return (void *) &cp[5]; + if (cp[4] == c) + return (void *) &cp[4]; +#endif + if (cp[3] == c) + return (void *) &cp[3]; + if (cp[2] == c) + return (void *) &cp[2]; + if (cp[1] == c) + return (void *) &cp[1]; + if (cp[0] == c) + return (void *) cp; + } + + n -= sizeof (longword); + } + + char_ptr = (const unsigned char *) longword_ptr; + + while (n-- > 0) + { + if (*--char_ptr == c) + return (void *) char_ptr; + } + + return 0; +} diff --git a/libc/string/generic/memset.c b/libc/string/generic/memset.c new file mode 100644 index 000000000..1fedb2c39 --- /dev/null +++ b/libc/string/generic/memset.c @@ -0,0 +1,90 @@ +/* Copyright (C) 1991, 1997, 2003 Free Software Foundation, Inc. + 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. */ + +#include <string.h> +#include "memcopy.h" + +#undef memset + +void * +memset (dstpp, c, len) + void *dstpp; + int c; + size_t len; +{ + long int dstp = (long int) dstpp; + + if (len >= 8) + { + size_t xlen; + op_t cccc; + + cccc = (unsigned char) c; + cccc |= cccc << 8; + cccc |= cccc << 16; + if (OPSIZ > 4) + /* Do the shift in two steps to avoid warning if long has 32 bits. */ + cccc |= (cccc << 16) << 16; + + /* There are at least some bytes to set. + No need to test for LEN == 0 in this alignment loop. */ + while (dstp % OPSIZ != 0) + { + ((byte *) dstp)[0] = c; + dstp += 1; + len -= 1; + } + + /* Write 8 `op_t' per iteration until less than 8 `op_t' remain. */ + xlen = len / (OPSIZ * 8); + while (xlen > 0) + { + ((op_t *) dstp)[0] = cccc; + ((op_t *) dstp)[1] = cccc; + ((op_t *) dstp)[2] = cccc; + ((op_t *) dstp)[3] = cccc; + ((op_t *) dstp)[4] = cccc; + ((op_t *) dstp)[5] = cccc; + ((op_t *) dstp)[6] = cccc; + ((op_t *) dstp)[7] = cccc; + dstp += 8 * OPSIZ; + xlen -= 1; + } + len %= OPSIZ * 8; + + /* Write 1 `op_t' per iteration until less than OPSIZ bytes remain. */ + xlen = len / OPSIZ; + while (xlen > 0) + { + ((op_t *) dstp)[0] = cccc; + dstp += OPSIZ; + xlen -= 1; + } + len %= OPSIZ; + } + + /* Write the last few bytes. */ + while (len > 0) + { + ((byte *) dstp)[0] = c; + dstp += 1; + len -= 1; + } + + return dstpp; +} diff --git a/libc/string/generic/pagecopy.h b/libc/string/generic/pagecopy.h new file mode 100644 index 000000000..5a0ada1fa --- /dev/null +++ b/libc/string/generic/pagecopy.h @@ -0,0 +1,75 @@ +/* Macros for copying by pages; used in memcpy, memmove. Generic macros. + Copyright (C) 1995, 1997 Free Software Foundation, Inc. + 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. */ + +/* This file defines the macro: + + PAGE_COPY_FWD_MAYBE (dstp, srcp, nbytes_left, nbytes) + + which is invoked like WORD_COPY_FWD et al. The pointers should be at + least word aligned. This will check if virtual copying by pages can and + should be done and do it if so. + + System-specific pagecopy.h files should define these macros and then + #include this file: + + PAGE_COPY_THRESHOLD + -- Minimum size for which virtual copying by pages is worthwhile. + + PAGE_SIZE + -- Size of a page. + + PAGE_COPY_FWD (dstp, srcp, nbytes_left, nbytes) + -- Macro to perform the virtual copy operation. + The pointers will be aligned to PAGE_SIZE bytes. +*/ + + +#if PAGE_COPY_THRESHOLD + +#include <assert.h> + +#define PAGE_COPY_FWD_MAYBE(dstp, srcp, nbytes_left, nbytes) \ + do \ + { \ + if ((nbytes) >= PAGE_COPY_THRESHOLD && \ + PAGE_OFFSET ((dstp) - (srcp)) == 0) \ + { \ + /* The amount to copy is past the threshold for copying \ + pages virtually with kernel VM operations, and the \ + source and destination addresses have the same alignment. */ \ + size_t nbytes_before = PAGE_OFFSET (-(dstp)); \ + if (nbytes_before != 0) \ + { \ + /* First copy the words before the first page boundary. */ \ + WORD_COPY_FWD (dstp, srcp, nbytes_left, nbytes_before); \ + assert (nbytes_left == 0); \ + nbytes -= nbytes_before; \ + } \ + PAGE_COPY_FWD (dstp, srcp, nbytes_left, nbytes); \ + } \ + } while (0) + +/* The page size is always a power of two, so we can avoid modulo division. */ +#define PAGE_OFFSET(n) ((n) & (PAGE_SIZE - 1)) + +#else + +#define PAGE_COPY_FWD_MAYBE(dstp, srcp, nbytes_left, nbytes) /* nada */ + +#endif diff --git a/libc/string/generic/rawmemchr.c b/libc/string/generic/rawmemchr.c new file mode 100644 index 000000000..0e2ac1c7e --- /dev/null +++ b/libc/string/generic/rawmemchr.c @@ -0,0 +1,160 @@ +/* Copyright (C) 1991,93,96,97,99,2000,2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Based on strlen implementation by Torbjorn Granlund (tege@sics.se), + with help from Dan Sahlin (dan@sics.se) and + commentary by Jim Blandy (jimb@ai.mit.edu); + adaptation to memchr suggested by Dick Karpinski (dick@cca.ucsf.edu), + and implemented by Roland McGrath (roland@ai.mit.edu). + + 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. */ + +#include <string.h> +#include <stdlib.h> +#include <limits.h> + +#include "memcopy.h" + +#define LONG_MAX_32_BITS 2147483647 + +#undef rawmemchr + +/* Find the first occurrence of C in S. */ +void *rawmemchr (const void * s, int c_in) +{ + const unsigned char *char_ptr; + const unsigned long int *longword_ptr; + unsigned long int longword, magic_bits, charmask; + unsigned reg_char c; + + c = (unsigned char) c_in; + + /* Handle the first few characters by reading one character at a time. + Do this until CHAR_PTR is aligned on a longword boundary. */ + for (char_ptr = (const unsigned char *) s; + ((unsigned long int) char_ptr & (sizeof (longword) - 1)) != 0; + ++char_ptr) + if (*char_ptr == c) + return (void *) char_ptr; + + /* All these elucidatory comments refer to 4-byte longwords, + but the theory applies equally well to 8-byte longwords. */ + + longword_ptr = (unsigned long int *) char_ptr; + + /* Bits 31, 24, 16, and 8 of this number are zero. Call these bits + the "holes." Note that there is a hole just to the left of + each byte, with an extra at the end: + + bits: 01111110 11111110 11111110 11111111 + bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD + + The 1-bits make sure that carries propagate to the next 0-bit. + The 0-bits provide holes for carries to fall into. */ + + if (sizeof (longword) != 4 && sizeof (longword) != 8) + abort (); + +#if LONG_MAX <= LONG_MAX_32_BITS + magic_bits = 0x7efefeff; +#else + magic_bits = ((unsigned long int) 0x7efefefe << 32) | 0xfefefeff; +#endif + + /* Set up a longword, each of whose bytes is C. */ + charmask = c | (c << 8); + charmask |= charmask << 16; +#if LONG_MAX > LONG_MAX_32_BITS + charmask |= charmask << 32; +#endif + + /* Instead of the traditional loop which tests each character, + we will test a longword at a time. The tricky part is testing + if *any of the four* bytes in the longword in question are zero. */ + while (1) + { + /* We tentatively exit the loop if adding MAGIC_BITS to + LONGWORD fails to change any of the hole bits of LONGWORD. + + 1) Is this safe? Will it catch all the zero bytes? + Suppose there is a byte with all zeros. Any carry bits + propagating from its left will fall into the hole at its + least significant bit and stop. Since there will be no + carry from its most significant bit, the LSB of the + byte to the left will be unchanged, and the zero will be + detected. + + 2) Is this worthwhile? Will it ignore everything except + zero bytes? Suppose every byte of LONGWORD has a bit set + somewhere. There will be a carry into bit 8. If bit 8 + is set, this will carry into bit 16. If bit 8 is clear, + one of bits 9-15 must be set, so there will be a carry + into bit 16. Similarly, there will be a carry into bit + 24. If one of bits 24-30 is set, there will be a carry + into bit 31, so all of the hole bits will be changed. + + The one misfire occurs when bits 24-30 are clear and bit + 31 is set; in this case, the hole at bit 31 is not + changed. If we had access to the processor carry flag, + we could close this loophole by putting the fourth hole + at bit 32! + + So it ignores everything except 128's, when they're aligned + properly. + + 3) But wait! Aren't we looking for C, not zero? + Good point. So what we do is XOR LONGWORD with a longword, + each of whose bytes is C. This turns each byte that is C + into a zero. */ + + longword = *longword_ptr++ ^ charmask; + + /* Add MAGIC_BITS to LONGWORD. */ + if ((((longword + magic_bits) + + /* Set those bits that were unchanged by the addition. */ + ^ ~longword) + + /* Look at only the hole bits. If any of the hole bits + are unchanged, most likely one of the bytes was a + zero. */ + & ~magic_bits) != 0) + { + /* Which of the bytes was C? If none of them were, it was + a misfire; continue the search. */ + + const unsigned char *cp = (const unsigned char *) (longword_ptr - 1); + + if (cp[0] == c) + return (void *) cp; + if (cp[1] == c) + return (void *) &cp[1]; + if (cp[2] == c) + return (void *) &cp[2]; + if (cp[3] == c) + return (void *) &cp[3]; +#if LONG_MAX > 2147483647 + if (cp[4] == c) + return (void *) &cp[4]; + if (cp[5] == c) + return (void *) &cp[5]; + if (cp[6] == c) + return (void *) &cp[6]; + if (cp[7] == c) + return (void *) &cp[7]; +#endif + } + } +} diff --git a/libc/string/generic/strcat.c b/libc/string/generic/strcat.c new file mode 100644 index 000000000..a9ed4a442 --- /dev/null +++ b/libc/string/generic/strcat.c @@ -0,0 +1,51 @@ +/* Copyright (C) 1991, 1997, 2003 Free Software Foundation, Inc. + 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. */ + +#include <string.h> +#include "memcopy.h" + +#undef strcat + +/* Append SRC on the end of DEST. */ +char * +strcat (dest, src) + char *dest; + const char *src; +{ + char *s1 = dest; + const char *s2 = src; + reg_char c; + + /* Find the end of the string. */ + do + c = *s1++; + while (c != '\0'); + + /* Make S1 point before the next character, so we can increment + it while memory is read (wins on pipelined cpus). */ + s1 -= 2; + + do + { + c = *s2++; + *++s1 = c; + } + while (c != '\0'); + + return dest; +} diff --git a/libc/string/generic/strchr.c b/libc/string/generic/strchr.c new file mode 100644 index 000000000..d0745060b --- /dev/null +++ b/libc/string/generic/strchr.c @@ -0,0 +1,190 @@ +/* Copyright (C) 1991,93,94,95,96,97,99,2000,03 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Based on strlen implementation by Torbjorn Granlund (tege@sics.se), + with help from Dan Sahlin (dan@sics.se) and + bug fix and commentary by Jim Blandy (jimb@ai.mit.edu); + adaptation to strchr suggested by Dick Karpinski (dick@cca.ucsf.edu), + and implemented by Roland McGrath (roland@ai.mit.edu). + + 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. */ + +#include <string.h> +#include <stdlib.h> + +#include "memcopy.h" + +#undef strchr + +/* Find the first occurrence of C in S. */ +char * +strchr (s, c_in) + const char *s; + int c_in; +{ + const unsigned char *char_ptr; + const unsigned long int *longword_ptr; + unsigned long int longword, magic_bits, charmask; + unsigned reg_char c; + + c = (unsigned char) c_in; + + /* Handle the first few characters by reading one character at a time. + Do this until CHAR_PTR is aligned on a longword boundary. */ + for (char_ptr = s; ((unsigned long int) char_ptr + & (sizeof (longword) - 1)) != 0; + ++char_ptr) + if (*char_ptr == c) + return (void *) char_ptr; + else if (*char_ptr == '\0') + return NULL; + + /* All these elucidatory comments refer to 4-byte longwords, + but the theory applies equally well to 8-byte longwords. */ + + longword_ptr = (unsigned long int *) char_ptr; + + /* Bits 31, 24, 16, and 8 of this number are zero. Call these bits + the "holes." Note that there is a hole just to the left of + each byte, with an extra at the end: + + bits: 01111110 11111110 11111110 11111111 + bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD + + The 1-bits make sure that carries propagate to the next 0-bit. + The 0-bits provide holes for carries to fall into. */ + switch (sizeof (longword)) + { + case 4: magic_bits = 0x7efefeffL; break; + case 8: magic_bits = ((0x7efefefeL << 16) << 16) | 0xfefefeffL; break; + default: + abort (); + } + + /* Set up a longword, each of whose bytes is C. */ + charmask = c | (c << 8); + charmask |= charmask << 16; + if (sizeof (longword) > 4) + /* Do the shift in two steps to avoid a warning if long has 32 bits. */ + charmask |= (charmask << 16) << 16; + if (sizeof (longword) > 8) + abort (); + + /* Instead of the traditional loop which tests each character, + we will test a longword at a time. The tricky part is testing + if *any of the four* bytes in the longword in question are zero. */ + for (;;) + { + /* We tentatively exit the loop if adding MAGIC_BITS to + LONGWORD fails to change any of the hole bits of LONGWORD. + + 1) Is this safe? Will it catch all the zero bytes? + Suppose there is a byte with all zeros. Any carry bits + propagating from its left will fall into the hole at its + least significant bit and stop. Since there will be no + carry from its most significant bit, the LSB of the + byte to the left will be unchanged, and the zero will be + detected. + + 2) Is this worthwhile? Will it ignore everything except + zero bytes? Suppose every byte of LONGWORD has a bit set + somewhere. There will be a carry into bit 8. If bit 8 + is set, this will carry into bit 16. If bit 8 is clear, + one of bits 9-15 must be set, so there will be a carry + into bit 16. Similarly, there will be a carry into bit + 24. If one of bits 24-30 is set, there will be a carry + into bit 31, so all of the hole bits will be changed. + + The one misfire occurs when bits 24-30 are clear and bit + 31 is set; in this case, the hole at bit 31 is not + changed. If we had access to the processor carry flag, + we could close this loophole by putting the fourth hole + at bit 32! + + So it ignores everything except 128's, when they're aligned + properly. + + 3) But wait! Aren't we looking for C as well as zero? + Good point. So what we do is XOR LONGWORD with a longword, + each of whose bytes is C. This turns each byte that is C + into a zero. */ + + longword = *longword_ptr++; + + /* Add MAGIC_BITS to LONGWORD. */ + if ((((longword + magic_bits) + + /* Set those bits that were unchanged by the addition. */ + ^ ~longword) + + /* Look at only the hole bits. If any of the hole bits + are unchanged, most likely one of the bytes was a + zero. */ + & ~magic_bits) != 0 || + + /* That caught zeroes. Now test for C. */ + ((((longword ^ charmask) + magic_bits) ^ ~(longword ^ charmask)) + & ~magic_bits) != 0) + { + /* Which of the bytes was C or zero? + If none of them were, it was a misfire; continue the search. */ + + const unsigned char *cp = (const unsigned char *) (longword_ptr - 1); + + if (*cp == c) + return (char *) cp; + else if (*cp == '\0') + return NULL; + if (*++cp == c) + return (char *) cp; + else if (*cp == '\0') + return NULL; + if (*++cp == c) + return (char *) cp; + else if (*cp == '\0') + return NULL; + if (*++cp == c) + return (char *) cp; + else if (*cp == '\0') + return NULL; + if (sizeof (longword) > 4) + { + if (*++cp == c) + return (char *) cp; + else if (*cp == '\0') + return NULL; + if (*++cp == c) + return (char *) cp; + else if (*cp == '\0') + return NULL; + if (*++cp == c) + return (char *) cp; + else if (*cp == '\0') + return NULL; + if (*++cp == c) + return (char *) cp; + else if (*cp == '\0') + return NULL; + } + } + } + + return NULL; +} + +#ifdef weak_alias +#undef index +weak_alias (strchr, index) +#endif diff --git a/libc/string/generic/strchrnul.c b/libc/string/generic/strchrnul.c new file mode 100644 index 000000000..908409a4c --- /dev/null +++ b/libc/string/generic/strchrnul.c @@ -0,0 +1,168 @@ +/* Copyright (C) 1991,93,94,95,96,97,99,2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Based on strlen implementation by Torbjorn Granlund (tege@sics.se), + with help from Dan Sahlin (dan@sics.se) and + bug fix and commentary by Jim Blandy (jimb@ai.mit.edu); + adaptation to strchr suggested by Dick Karpinski (dick@cca.ucsf.edu), + and implemented by Roland McGrath (roland@ai.mit.edu). + + 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. */ + +#include <string.h> +#include <stdlib.h> + +#include "memcopy.h" + +#undef __strchrnul +#undef strchrnul + +/* Find the first occurrence of C in S or the final NUL byte. */ +char *__strchrnul (const char *s, int c_in) +{ + const unsigned char *char_ptr; + const unsigned long int *longword_ptr; + unsigned long int longword, magic_bits, charmask; + unsigned reg_char c; + + c = (unsigned char) c_in; + + /* Handle the first few characters by reading one character at a time. + Do this until CHAR_PTR is aligned on a longword boundary. */ + for (char_ptr = s; ((unsigned long int) char_ptr + & (sizeof (longword) - 1)) != 0; + ++char_ptr) + if (*char_ptr == c || *char_ptr == '\0') + return (void *) char_ptr; + + /* All these elucidatory comments refer to 4-byte longwords, + but the theory applies equally well to 8-byte longwords. */ + + longword_ptr = (unsigned long int *) char_ptr; + + /* Bits 31, 24, 16, and 8 of this number are zero. Call these bits + the "holes." Note that there is a hole just to the left of + each byte, with an extra at the end: + + bits: 01111110 11111110 11111110 11111111 + bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD + + The 1-bits make sure that carries propagate to the next 0-bit. + The 0-bits provide holes for carries to fall into. */ + switch (sizeof (longword)) + { + case 4: magic_bits = 0x7efefeffL; break; + case 8: magic_bits = ((0x7efefefeL << 16) << 16) | 0xfefefeffL; break; + default: + abort (); + } + + /* Set up a longword, each of whose bytes is C. */ + charmask = c | (c << 8); + charmask |= charmask << 16; + if (sizeof (longword) > 4) + /* Do the shift in two steps to avoid a warning if long has 32 bits. */ + charmask |= (charmask << 16) << 16; + if (sizeof (longword) > 8) + abort (); + + /* Instead of the traditional loop which tests each character, + we will test a longword at a time. The tricky part is testing + if *any of the four* bytes in the longword in question are zero. */ + for (;;) + { + /* We tentatively exit the loop if adding MAGIC_BITS to + LONGWORD fails to change any of the hole bits of LONGWORD. + + 1) Is this safe? Will it catch all the zero bytes? + Suppose there is a byte with all zeros. Any carry bits + propagating from its left will fall into the hole at its + least significant bit and stop. Since there will be no + carry from its most significant bit, the LSB of the + byte to the left will be unchanged, and the zero will be + detected. + + 2) Is this worthwhile? Will it ignore everything except + zero bytes? Suppose every byte of LONGWORD has a bit set + somewhere. There will be a carry into bit 8. If bit 8 + is set, this will carry into bit 16. If bit 8 is clear, + one of bits 9-15 must be set, so there will be a carry + into bit 16. Similarly, there will be a carry into bit + 24. If one of bits 24-30 is set, there will be a carry + into bit 31, so all of the hole bits will be changed. + + The one misfire occurs when bits 24-30 are clear and bit + 31 is set; in this case, the hole at bit 31 is not + changed. If we had access to the processor carry flag, + we could close this loophole by putting the fourth hole + at bit 32! + + So it ignores everything except 128's, when they're aligned + properly. + + 3) But wait! Aren't we looking for C as well as zero? + Good point. So what we do is XOR LONGWORD with a longword, + each of whose bytes is C. This turns each byte that is C + into a zero. */ + + longword = *longword_ptr++; + + /* Add MAGIC_BITS to LONGWORD. */ + if ((((longword + magic_bits) + + /* Set those bits that were unchanged by the addition. */ + ^ ~longword) + + /* Look at only the hole bits. If any of the hole bits + are unchanged, most likely one of the bytes was a + zero. */ + & ~magic_bits) != 0 || + + /* That caught zeroes. Now test for C. */ + ((((longword ^ charmask) + magic_bits) ^ ~(longword ^ charmask)) + & ~magic_bits) != 0) + { + /* Which of the bytes was C or zero? + If none of them were, it was a misfire; continue the search. */ + + const unsigned char *cp = (const unsigned char *) (longword_ptr - 1); + + if (*cp == c || *cp == '\0') + return (char *) cp; + if (*++cp == c || *cp == '\0') + return (char *) cp; + if (*++cp == c || *cp == '\0') + return (char *) cp; + if (*++cp == c || *cp == '\0') + return (char *) cp; + if (sizeof (longword) > 4) + { + if (*++cp == c || *cp == '\0') + return (char *) cp; + if (*++cp == c || *cp == '\0') + return (char *) cp; + if (*++cp == c || *cp == '\0') + return (char *) cp; + if (*++cp == c || *cp == '\0') + return (char *) cp; + } + } + } + + /* This should never happen. */ + return NULL; +} + +weak_alias (__strchrnul, strchrnul) diff --git a/libc/string/generic/strcmp.c b/libc/string/generic/strcmp.c new file mode 100644 index 000000000..2af550d81 --- /dev/null +++ b/libc/string/generic/strcmp.c @@ -0,0 +1,52 @@ +/* Copyright (C) 1991, 1996, 1997, 2003 Free Software Foundation, Inc. + 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. */ + +#include <string.h> +#include <locale.h> + +#include "memcopy.h" + +#undef strcmp + +#ifdef __LOCALE_C_ONLY +weak_alias(strcmp,strcoll); +#endif /* __LOCALE_C_ONLY */ + +/* Compare S1 and S2, returning less than, equal to or + greater than zero if S1 is lexicographically less than, + equal to or greater than S2. */ +int +strcmp (p1, p2) + const char *p1; + const char *p2; +{ + register const unsigned char *s1 = (const unsigned char *) p1; + register const unsigned char *s2 = (const unsigned char *) p2; + unsigned reg_char c1, c2; + + do + { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0') + return c1 - c2; + } + while (c1 == c2); + + return c1 - c2; +} diff --git a/libc/string/generic/strcpy.c b/libc/string/generic/strcpy.c new file mode 100644 index 000000000..7e40aab3f --- /dev/null +++ b/libc/string/generic/strcpy.c @@ -0,0 +1,50 @@ +/* Copyright (C) 1991, 1997, 2000, 2003 Free Software Foundation, Inc. + 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. */ + +#include <string.h> +#include <stddef.h> + +#include "memcopy.h" +#include "bp-checks.h" + +#undef strcpy + +/* Copy SRC to DEST. */ +char * +strcpy (dest, src) + char *dest; + const char *src; +{ + reg_char c; + char *__unbounded s = (char *__unbounded) CHECK_BOUNDS_LOW (src); + const ptrdiff_t off = CHECK_BOUNDS_LOW (dest) - s - 1; + size_t n; + + do + { + c = *s++; + s[off] = c; + } + while (c != '\0'); + + n = s - src; + (void) CHECK_BOUNDS_HIGH (src + n); + (void) CHECK_BOUNDS_HIGH (dest + n); + + return dest; +} diff --git a/libc/string/generic/strcspn.c b/libc/string/generic/strcspn.c new file mode 100644 index 000000000..c5d2d769d --- /dev/null +++ b/libc/string/generic/strcspn.c @@ -0,0 +1,39 @@ +/* Copyright (C) 1991, 1994, 1996, 1997, 2003 Free Software Foundation, Inc. + 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. */ + +#include <string.h> + +#undef strcspn + +/* Return the length of the maximum initial segment of S + which contains no characters from REJECT. */ +size_t +strcspn (s, reject) + const char *s; + const char *reject; +{ + size_t count = 0; + + while (*s != '\0') + if (strchr (reject, *s++) == NULL) + ++count; + else + return count; + + return count; +} diff --git a/libc/string/generic/strlen.c b/libc/string/generic/strlen.c new file mode 100644 index 000000000..2bbf8203f --- /dev/null +++ b/libc/string/generic/strlen.c @@ -0,0 +1,152 @@ +/* Copyright (C) 1991, 1993, 1997, 2000, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Torbjorn Granlund (tege@sics.se), + with help from Dan Sahlin (dan@sics.se); + commentary by Jim Blandy (jimb@ai.mit.edu). + + 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. */ + +#include <string.h> +#include <stdlib.h> + +#undef strlen + +/* Return the length of the null-terminated string STR. Scan for + the null terminator quickly by testing four bytes at a time. */ +size_t +strlen (str) + const char *str; +{ + const char *char_ptr; + const unsigned long int *longword_ptr; + unsigned long int longword, magic_bits, himagic, lomagic; + + /* Handle the first few characters by reading one character at a time. + Do this until CHAR_PTR is aligned on a longword boundary. */ + for (char_ptr = str; ((unsigned long int) char_ptr + & (sizeof (longword) - 1)) != 0; + ++char_ptr) + if (*char_ptr == '\0') + return char_ptr - str; + + /* All these elucidatory comments refer to 4-byte longwords, + but the theory applies equally well to 8-byte longwords. */ + + longword_ptr = (unsigned long int *) char_ptr; + + /* Bits 31, 24, 16, and 8 of this number are zero. Call these bits + the "holes." Note that there is a hole just to the left of + each byte, with an extra at the end: + + bits: 01111110 11111110 11111110 11111111 + bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD + + The 1-bits make sure that carries propagate to the next 0-bit. + The 0-bits provide holes for carries to fall into. */ + magic_bits = 0x7efefeffL; + himagic = 0x80808080L; + lomagic = 0x01010101L; + if (sizeof (longword) > 4) + { + /* 64-bit version of the magic. */ + /* Do the shift in two steps to avoid a warning if long has 32 bits. */ + magic_bits = ((0x7efefefeL << 16) << 16) | 0xfefefeffL; + himagic = ((himagic << 16) << 16) | himagic; + lomagic = ((lomagic << 16) << 16) | lomagic; + } + if (sizeof (longword) > 8) + abort (); + + /* Instead of the traditional loop which tests each character, + we will test a longword at a time. The tricky part is testing + if *any of the four* bytes in the longword in question are zero. */ + for (;;) + { + /* We tentatively exit the loop if adding MAGIC_BITS to + LONGWORD fails to change any of the hole bits of LONGWORD. + + 1) Is this safe? Will it catch all the zero bytes? + Suppose there is a byte with all zeros. Any carry bits + propagating from its left will fall into the hole at its + least significant bit and stop. Since there will be no + carry from its most significant bit, the LSB of the + byte to the left will be unchanged, and the zero will be + detected. + + 2) Is this worthwhile? Will it ignore everything except + zero bytes? Suppose every byte of LONGWORD has a bit set + somewhere. There will be a carry into bit 8. If bit 8 + is set, this will carry into bit 16. If bit 8 is clear, + one of bits 9-15 must be set, so there will be a carry + into bit 16. Similarly, there will be a carry into bit + 24. If one of bits 24-30 is set, there will be a carry + into bit 31, so all of the hole bits will be changed. + + The one misfire occurs when bits 24-30 are clear and bit + 31 is set; in this case, the hole at bit 31 is not + changed. If we had access to the processor carry flag, + we could close this loophole by putting the fourth hole + at bit 32! + + So it ignores everything except 128's, when they're aligned + properly. */ + + longword = *longword_ptr++; + + if ( +#if 0 + /* Add MAGIC_BITS to LONGWORD. */ + (((longword + magic_bits) + + /* Set those bits that were unchanged by the addition. */ + ^ ~longword) + + /* Look at only the hole bits. If any of the hole bits + are unchanged, most likely one of the bytes was a + zero. */ + & ~magic_bits) +#else + ((longword - lomagic) & himagic) +#endif + != 0) + { + /* Which of the bytes was the zero? If none of them were, it was + a misfire; continue the search. */ + + const char *cp = (const char *) (longword_ptr - 1); + + if (cp[0] == 0) + return cp - str; + if (cp[1] == 0) + return cp - str + 1; + if (cp[2] == 0) + return cp - str + 2; + if (cp[3] == 0) + return cp - str + 3; + if (sizeof (longword) > 4) + { + if (cp[4] == 0) + return cp - str + 4; + if (cp[5] == 0) + return cp - str + 5; + if (cp[6] == 0) + return cp - str + 6; + if (cp[7] == 0) + return cp - str + 7; + } + } + } +} diff --git a/libc/string/generic/strncat.c b/libc/string/generic/strncat.c new file mode 100644 index 000000000..a3d283bf7 --- /dev/null +++ b/libc/string/generic/strncat.c @@ -0,0 +1,81 @@ +/* Copyright (C) 1991, 1997 Free Software Foundation, Inc. + 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. */ + +#include <string.h> + +#include "memcopy.h" + +#undef strncat + +char * +strncat (s1, s2, n) + char *s1; + const char *s2; + size_t n; +{ + reg_char c; + char *s = s1; + + /* Find the end of S1. */ + do + c = *s1++; + while (c != '\0'); + + /* Make S1 point before next character, so we can increment + it while memory is read (wins on pipelined cpus). */ + s1 -= 2; + + if (n >= 4) + { + size_t n4 = n >> 2; + do + { + c = *s2++; + *++s1 = c; + if (c == '\0') + return s; + c = *s2++; + *++s1 = c; + if (c == '\0') + return s; + c = *s2++; + *++s1 = c; + if (c == '\0') + return s; + c = *s2++; + *++s1 = c; + if (c == '\0') + return s; + } while (--n4 > 0); + n &= 3; + } + + while (n > 0) + { + c = *s2++; + *++s1 = c; + if (c == '\0') + return s; + n--; + } + + if (c != '\0') + *++s1 = '\0'; + + return s; +} diff --git a/libc/string/generic/strncmp.c b/libc/string/generic/strncmp.c new file mode 100644 index 000000000..d9964a883 --- /dev/null +++ b/libc/string/generic/strncmp.c @@ -0,0 +1,72 @@ +/* Copyright (C) 1991, 1996, 1997, 2003 Free Software Foundation, Inc. + 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. */ + +#include <string.h> +#include "memcopy.h" + +#undef strncmp + +/* Compare no more than N characters of S1 and S2, + returning less than, equal to or greater than zero + if S1 is lexicographically less than, equal to or + greater than S2. */ +int +strncmp (s1, s2, n) + const char *s1; + const char *s2; + size_t n; +{ + unsigned reg_char c1 = '\0'; + unsigned reg_char c2 = '\0'; + + if (n >= 4) + { + size_t n4 = n >> 2; + do + { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + } while (--n4 > 0); + n &= 3; + } + + while (n > 0) + { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + n--; + } + + return c1 - c2; +} diff --git a/libc/string/generic/strncpy.c b/libc/string/generic/strncpy.c new file mode 100644 index 000000000..214f38430 --- /dev/null +++ b/libc/string/generic/strncpy.c @@ -0,0 +1,86 @@ +/* Copyright (C) 1991, 1997, 2003 Free Software Foundation, Inc. + 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. */ + +#include <string.h> +#include "memcopy.h" + +#undef strncpy + +char * +strncpy (s1, s2, n) + char *s1; + const char *s2; + size_t n; +{ + reg_char c; + char *s = s1; + + --s1; + + if (n >= 4) + { + size_t n4 = n >> 2; + + for (;;) + { + c = *s2++; + *++s1 = c; + if (c == '\0') + break; + c = *s2++; + *++s1 = c; + if (c == '\0') + break; + c = *s2++; + *++s1 = c; + if (c == '\0') + break; + c = *s2++; + *++s1 = c; + if (c == '\0') + break; + if (--n4 == 0) + goto last_chars; + } + n = n - (s1 - s) - 1; + if (n == 0) + return s; + goto zero_fill; + } + + last_chars: + n &= 3; + if (n == 0) + return s; + + do + { + c = *s2++; + *++s1 = c; + if (--n == 0) + return s; + } + while (c != '\0'); + + zero_fill: + do + *++s1 = '\0'; + while (--n > 0); + + return s; +} diff --git a/libc/string/generic/strnlen.c b/libc/string/generic/strnlen.c new file mode 100644 index 000000000..bdd3bb475 --- /dev/null +++ b/libc/string/generic/strnlen.c @@ -0,0 +1,158 @@ +/* Find the length of STRING, but scan at most MAXLEN characters. + Copyright (C) 1991, 1993, 1997, 2000, 2001 Free Software Foundation, Inc. + Contributed by Jakub Jelinek <jakub@redhat.com>. + + Based on strlen written by Torbjorn Granlund (tege@sics.se), + with help from Dan Sahlin (dan@sics.se); + commentary by Jim Blandy (jimb@ai.mit.edu). + + 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; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <string.h> +#include <stdlib.h> + +/* Find the length of S, but scan at most MAXLEN characters. If no + '\0' terminator is found in that many characters, return MAXLEN. */ +size_t strnlen (const char *str, size_t maxlen) +{ + const char *char_ptr, *end_ptr = str + maxlen; + const unsigned long int *longword_ptr; + unsigned long int longword, magic_bits, himagic, lomagic; + + if (maxlen == 0) + return 0; + + if (__builtin_expect (end_ptr < str, 0)) + end_ptr = (const char *) ~0UL; + + /* Handle the first few characters by reading one character at a time. + Do this until CHAR_PTR is aligned on a longword boundary. */ + for (char_ptr = str; ((unsigned long int) char_ptr + & (sizeof (longword) - 1)) != 0; + ++char_ptr) + if (*char_ptr == '\0') + { + if (char_ptr > end_ptr) + char_ptr = end_ptr; + return char_ptr - str; + } + + /* All these elucidatory comments refer to 4-byte longwords, + but the theory applies equally well to 8-byte longwords. */ + + longword_ptr = (unsigned long int *) char_ptr; + + /* Bits 31, 24, 16, and 8 of this number are zero. Call these bits + the "holes." Note that there is a hole just to the left of + each byte, with an extra at the end: + + bits: 01111110 11111110 11111110 11111111 + bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD + + The 1-bits make sure that carries propagate to the next 0-bit. + The 0-bits provide holes for carries to fall into. */ + magic_bits = 0x7efefeffL; + himagic = 0x80808080L; + lomagic = 0x01010101L; + if (sizeof (longword) > 4) + { + /* 64-bit version of the magic. */ + /* Do the shift in two steps to avoid a warning if long has 32 bits. */ + magic_bits = ((0x7efefefeL << 16) << 16) | 0xfefefeffL; + himagic = ((himagic << 16) << 16) | himagic; + lomagic = ((lomagic << 16) << 16) | lomagic; + } + if (sizeof (longword) > 8) + abort (); + + /* Instead of the traditional loop which tests each character, + we will test a longword at a time. The tricky part is testing + if *any of the four* bytes in the longword in question are zero. */ + while (longword_ptr < (unsigned long int *) end_ptr) + { + /* We tentatively exit the loop if adding MAGIC_BITS to + LONGWORD fails to change any of the hole bits of LONGWORD. + + 1) Is this safe? Will it catch all the zero bytes? + Suppose there is a byte with all zeros. Any carry bits + propagating from its left will fall into the hole at its + least significant bit and stop. Since there will be no + carry from its most significant bit, the LSB of the + byte to the left will be unchanged, and the zero will be + detected. + + 2) Is this worthwhile? Will it ignore everything except + zero bytes? Suppose every byte of LONGWORD has a bit set + somewhere. There will be a carry into bit 8. If bit 8 + is set, this will carry into bit 16. If bit 8 is clear, + one of bits 9-15 must be set, so there will be a carry + into bit 16. Similarly, there will be a carry into bit + 24. If one of bits 24-30 is set, there will be a carry + into bit 31, so all of the hole bits will be changed. + + The one misfire occurs when bits 24-30 are clear and bit + 31 is set; in this case, the hole at bit 31 is not + changed. If we had access to the processor carry flag, + we could close this loophole by putting the fourth hole + at bit 32! + + So it ignores everything except 128's, when they're aligned + properly. */ + + longword = *longword_ptr++; + + if ((longword - lomagic) & himagic) + { + /* Which of the bytes was the zero? If none of them were, it was + a misfire; continue the search. */ + + const char *cp = (const char *) (longword_ptr - 1); + + char_ptr = cp; + if (cp[0] == 0) + break; + char_ptr = cp + 1; + if (cp[1] == 0) + break; + char_ptr = cp + 2; + if (cp[2] == 0) + break; + char_ptr = cp + 3; + if (cp[3] == 0) + break; + if (sizeof (longword) > 4) + { + char_ptr = cp + 4; + if (cp[4] == 0) + break; + char_ptr = cp + 5; + if (cp[5] == 0) + break; + char_ptr = cp + 6; + if (cp[6] == 0) + break; + char_ptr = cp + 7; + if (cp[7] == 0) + break; + } + } + char_ptr = end_ptr; + } + + if (char_ptr > end_ptr) + char_ptr = end_ptr; + return char_ptr - str; +} diff --git a/libc/string/generic/strrchr.c b/libc/string/generic/strrchr.c new file mode 100644 index 000000000..8f815e9ff --- /dev/null +++ b/libc/string/generic/strrchr.c @@ -0,0 +1,49 @@ +/* Copyright (C) 1991, 1995, 1996, 1997, 2003 Free Software Foundation, Inc. + 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. */ + +#include <string.h> + +#undef strrchr + +/* Find the last occurrence of C in S. */ +char * +strrchr (const char *s, int c) +{ + register const char *found, *p; + + c = (unsigned char) c; + + /* Since strchr is fast, we use it rather than the obvious loop. */ + + if (c == '\0') + return strchr (s, '\0'); + + found = NULL; + while ((p = strchr (s, c)) != NULL) + { + found = p; + s = p + 1; + } + + return (char *) found; +} + +#ifdef weak_alias +#undef rindex +weak_alias (strrchr, rindex) +#endif diff --git a/libc/string/generic/strsep.c b/libc/string/generic/strsep.c new file mode 100644 index 000000000..345c722bf --- /dev/null +++ b/libc/string/generic/strsep.c @@ -0,0 +1,65 @@ +/* Copyright (C) 1992, 93, 96, 97, 98, 99, 2004 Free Software Foundation, Inc. + 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. */ + +#include <string.h> + +#undef strsep + +char *strsep (char **stringp, const char *delim) +{ + char *begin, *end; + + begin = *stringp; + if (begin == NULL) + return NULL; + + /* A frequent case is when the delimiter string contains only one + character. Here we don't need to call the expensive `strpbrk' + function and instead work using `strchr'. */ + if (delim[0] == '\0' || delim[1] == '\0') + { + char ch = delim[0]; + + if (ch == '\0') + end = NULL; + else + { + if (*begin == ch) + end = begin; + else if (*begin == '\0') + end = NULL; + else + end = strchr (begin + 1, ch); + } + } + else + /* Find the end of the token. */ + end = strpbrk (begin, delim); + + if (end) + { + /* Terminate the token and set *STRINGP past NUL character. */ + *end++ = '\0'; + *stringp = end; + } + else + /* No more delimiters; this is the last token. */ + *stringp = NULL; + + return begin; +} diff --git a/libc/string/generic/strspn.c b/libc/string/generic/strspn.c new file mode 100644 index 000000000..dcf8b135d --- /dev/null +++ b/libc/string/generic/strspn.c @@ -0,0 +1,46 @@ +/* Copyright (C) 1991, 1997, 2003 Free Software Foundation, Inc. + 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. */ + +#include <string.h> + +#undef strspn + +/* Return the length of the maximum initial segment + of S which contains only characters in ACCEPT. */ +size_t +strspn (s, accept) + const char *s; + const char *accept; +{ + const char *p; + const char *a; + size_t count = 0; + + for (p = s; *p != '\0'; ++p) + { + for (a = accept; *a != '\0'; ++a) + if (*p == *a) + break; + if (*a == '\0') + return count; + else + ++count; + } + + return count; +} diff --git a/libc/string/generic/strstr.c b/libc/string/generic/strstr.c new file mode 100644 index 000000000..45a64bb9b --- /dev/null +++ b/libc/string/generic/strstr.c @@ -0,0 +1,116 @@ +/* Return the offset of one string within another. + Copyright (C) 1994,1996,1997,2000,2001,2003 Free Software Foundation, Inc. + 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. */ + +/* + * My personal strstr() implementation that beats most other algorithms. + * Until someone tells me otherwise, I assume that this is the + * fastest implementation of strstr() in C. + * I deliberately chose not to comment it. You should have at least + * as much fun trying to understand it, as I had to write it :-). + * + * Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de */ + +#include <string.h> + +typedef unsigned chartype; + +#undef strstr + +char * +strstr (phaystack, pneedle) + const char *phaystack; + const char *pneedle; +{ + const unsigned char *haystack, *needle; + chartype b; + const unsigned char *rneedle; + + haystack = (const unsigned char *) phaystack; + + if ((b = *(needle = (const unsigned char *) pneedle))) + { + chartype c; + haystack--; /* possible ANSI violation */ + + { + chartype a; + do + if (!(a = *++haystack)) + goto ret0; + while (a != b); + } + + if (!(c = *++needle)) + goto foundneedle; + ++needle; + goto jin; + + for (;;) + { + { + chartype a; + if (0) + jin:{ + if ((a = *++haystack) == c) + goto crest; + } + else + a = *++haystack; + do + { + for (; a != b; a = *++haystack) + { + if (!a) + goto ret0; + if ((a = *++haystack) == b) + break; + if (!a) + goto ret0; + } + } + while ((a = *++haystack) != c); + } + crest: + { + chartype a; + { + const unsigned char *rhaystack; + if (*(rhaystack = haystack-- + 1) == (a = *(rneedle = needle))) + do + { + if (!a) + goto foundneedle; + if (*++rhaystack != (a = *++needle)) + break; + if (!a) + goto foundneedle; + } + while (*++rhaystack == (a = *++needle)); + needle = rneedle; /* took the register-poor aproach */ + } + if (!a) + break; + } + } + } +foundneedle: + return (char *) haystack; +ret0: + return 0; +} diff --git a/libc/string/generic/strtok_r.c b/libc/string/generic/strtok_r.c new file mode 100644 index 000000000..15ca8b4ba --- /dev/null +++ b/libc/string/generic/strtok_r.c @@ -0,0 +1,69 @@ +/* Reentrant string tokenizer. Generic version. + Copyright (C) 1991,1996-1999,2001,2004 Free Software Foundation, Inc. + 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. */ + +#define _GNU_SOURCE +#include <string.h> + +#undef strtok_r +#undef __strtok_r + +/* Parse S into tokens separated by characters in DELIM. + If S is NULL, the saved pointer in SAVE_PTR is used as + the next starting point. For example: + char s[] = "-abc-=-def"; + char *sp; + x = strtok_r(s, "-", &sp); // x = "abc", sp = "=-def" + x = strtok_r(NULL, "-=", &sp); // x = "def", sp = NULL + x = strtok_r(NULL, "=", &sp); // x = NULL + // s = "abc\0-def\0" +*/ +char * +__strtok_r (s, delim, save_ptr) + char *s; + const char *delim; + char **save_ptr; +{ + char *token; + + if (s == NULL) + s = *save_ptr; + + /* Scan leading delimiters. */ + s += strspn (s, delim); + if (*s == '\0') + { + *save_ptr = s; + return NULL; + } + + /* Find the end of the token. */ + token = s; + s = strpbrk (token, delim); + if (s == NULL) + /* This token finishes the string. */ + *save_ptr = rawmemchr (token, '\0'); + else + { + /* Terminate the token and make *SAVE_PTR point past it. */ + *s = '\0'; + *save_ptr = s + 1; + } + return token; +} +weak_alias (__strtok_r, strtok_r) diff --git a/libc/string/mips/Makefile b/libc/string/mips/Makefile new file mode 100644 index 000000000..a17826ea5 --- /dev/null +++ b/libc/string/mips/Makefile @@ -0,0 +1,38 @@ +# Makefile for uClibc +# +# Copyright (C) 2000-2003 Erik Andersen <andersen@uclibc.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Library General Public License as published by the Free +# Software Foundation; either version 2 of the License, or (at your option) any +# later version. +# +# This program 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 Library General Public License for more +# details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +TOPDIR=../../../ +include $(TOPDIR)Rules.mak + +SSRC= memcpy.S memset.S +SOBJS=$(patsubst %.S,%.o, $(SSRC)) + +all: $(SOBJS) $(LIBC) + +$(LIBC): ar-target + +ar-target: $(SOBJS) + $(AR) $(ARFLAGS) $(LIBC) $(SOBJS) + +$(SOBJS): %.o : %.S + $(CC) $(CFLAGS) -c $< -o $@ + $(STRIPTOOL) -x -R .note -R .comment $*.o + +clean: + $(RM) *.[oa] *~ core + diff --git a/libc/string/mips/memcpy.S b/libc/string/mips/memcpy.S new file mode 100644 index 000000000..369c82f39 --- /dev/null +++ b/libc/string/mips/memcpy.S @@ -0,0 +1,140 @@ +/* Copyright (C) 2002, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Hartvig Ekner <hartvige@mips.com>, 2002. + + 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. */ + +/*#include <sysdep.h>*/ +#include <endian.h> +#include "sysdep.h" + +#ifdef __mips64 +#error mips32 code being compiled for mips64! +#endif + +/* void *memcpy(void *s1, const void *s2, size_t n); */ + +#if __BYTE_ORDER == __BIG_ENDIAN +# define LWHI lwl /* high part is left in big-endian */ +# define SWHI swl /* high part is left in big-endian */ +# define LWLO lwr /* low part is right in big-endian */ +# define SWLO swr /* low part is right in big-endian */ +#else +# define LWHI lwr /* high part is right in little-endian */ +# define SWHI swr /* high part is right in little-endian */ +# define LWLO lwl /* low part is left in little-endian */ +# define SWLO swl /* low part is left in little-endian */ +#endif + +ENTRY (memcpy) + .set noreorder + + slti t0, a2, 8 # Less than 8? + bne t0, zero, L(last8) + move v0, a0 # Setup exit value before too late + + xor t0, a1, a0 # Find a0/a1 displacement + andi t0, 0x3 + bne t0, zero, L(shift) # Go handle the unaligned case + subu t1, zero, a1 + andi t1, 0x3 # a0/a1 are aligned, but are we + beq t1, zero, L(chk8w) # starting in the middle of a word? + subu a2, t1 + LWHI t0, 0(a1) # Yes we are... take care of that + addu a1, t1 + SWHI t0, 0(a0) + addu a0, t1 + +L(chk8w): + andi t0, a2, 0x1f # 32 or more bytes left? + beq t0, a2, L(chk1w) + subu a3, a2, t0 # Yes + addu a3, a1 # a3 = end address of loop + move a2, t0 # a2 = what will be left after loop +L(lop8w): + lw t0, 0(a1) # Loop taking 8 words at a time + lw t1, 4(a1) + lw t2, 8(a1) + lw t3, 12(a1) + lw t4, 16(a1) + lw t5, 20(a1) + lw t6, 24(a1) + lw t7, 28(a1) + addiu a0, 32 + addiu a1, 32 + sw t0, -32(a0) + sw t1, -28(a0) + sw t2, -24(a0) + sw t3, -20(a0) + sw t4, -16(a0) + sw t5, -12(a0) + sw t6, -8(a0) + bne a1, a3, L(lop8w) + sw t7, -4(a0) + +L(chk1w): + andi t0, a2, 0x3 # 4 or more bytes left? + beq t0, a2, L(last8) + subu a3, a2, t0 # Yes, handle them one word at a time + addu a3, a1 # a3 again end address + move a2, t0 +L(lop1w): + lw t0, 0(a1) + addiu a0, 4 + addiu a1, 4 + bne a1, a3, L(lop1w) + sw t0, -4(a0) + +L(last8): + blez a2, L(lst8e) # Handle last 8 bytes, one at a time + addu a3, a2, a1 +L(lst8l): + lb t0, 0(a1) + addiu a0, 1 + addiu a1, 1 + bne a1, a3, L(lst8l) + sb t0, -1(a0) +L(lst8e): + jr ra # Bye, bye + nop + +L(shift): + subu a3, zero, a0 # Src and Dest unaligned + andi a3, 0x3 # (unoptimized case...) + beq a3, zero, L(shft1) + subu a2, a3 # a2 = bytes left + LWHI t0, 0(a1) # Take care of first odd part + LWLO t0, 3(a1) + addu a1, a3 + SWHI t0, 0(a0) + addu a0, a3 +L(shft1): + andi t0, a2, 0x3 + subu a3, a2, t0 + addu a3, a1 +L(shfth): + LWHI t1, 0(a1) # Limp through, word by word + LWLO t1, 3(a1) + addiu a0, 4 + addiu a1, 4 + bne a1, a3, L(shfth) + sw t1, -4(a0) + b L(last8) # Handle anything which may be left + move a2, t0 + + .set reorder +END (memcpy) +libc_hidden_builtin_def (memcpy) diff --git a/libc/string/mips/memset.S b/libc/string/mips/memset.S new file mode 100644 index 000000000..0919fb82e --- /dev/null +++ b/libc/string/mips/memset.S @@ -0,0 +1,90 @@ +/* Copyright (C) 2002, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Hartvig Ekner <hartvige@mips.com>, 2002. + + 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. */ + +/*#include <sysdep.h>*/ +#include <endian.h> +#include "sysdep.h" + +#ifdef __mips64 +#error mips32 code being compiled for mips64! +#endif + +/* void *memset(void *s, int c, size_t n). */ + +#if __BYTE_ORDER == __BIG_ENDIAN +# define SWHI swl /* high part is left in big-endian */ +#else +# define SWHI swr /* high part is right in little-endian */ +#endif + +ENTRY (memset) + .set noreorder + + slti t1, a2, 8 # Less than 8? + bne t1, zero, L(last8) + move v0, a0 # Setup exit value before too late + + beq a1, zero, L(ueven) # If zero pattern, no need to extend + andi a1, 0xff # Avoid problems with bogus arguments + sll t0, a1, 8 + or a1, t0 + sll t0, a1, 16 + or a1, t0 # a1 is now pattern in full word + +L(ueven): + subu t0, zero, a0 # Unaligned address? + andi t0, 0x3 + beq t0, zero, L(chkw) + subu a2, t0 + SWHI a1, 0(a0) # Yes, handle first unaligned part + addu a0, t0 # Now both a0 and a2 are updated + +L(chkw): + andi t0, a2, 0x7 # Enough left for one loop iteration? + beq t0, a2, L(chkl) + subu a3, a2, t0 + addu a3, a0 # a3 is last loop address +1 + move a2, t0 # a2 is now # of bytes left after loop +L(loopw): + addiu a0, 8 # Handle 2 words pr. iteration + sw a1, -8(a0) + bne a0, a3, L(loopw) + sw a1, -4(a0) + +L(chkl): + andi t0, a2, 0x4 # Check if there is at least a full + beq t0, zero, L(last8) # word remaining after the loop + subu a2, t0 + sw a1, 0(a0) # Yes... + addiu a0, 4 + +L(last8): + blez a2, L(exit) # Handle last 8 bytes (if cnt>0) + addu a3, a2, a0 # a3 is last address +1 +L(lst8l): + addiu a0, 1 + bne a0, a3, L(lst8l) + sb a1, -1(a0) +L(exit): + j ra # Bye, bye + nop + + .set reorder +END (memset) +libc_hidden_builtin_def (memset) diff --git a/libc/string/mips/sysdep.h b/libc/string/mips/sysdep.h new file mode 100644 index 000000000..2c96ced0d --- /dev/null +++ b/libc/string/mips/sysdep.h @@ -0,0 +1,51 @@ +/* Adapted from glibc's sysdeps/unix/mips/sysdep.h */ + +/* Copyright (C) 1992, 1995, 1997, 1999, 2000, 2002, 2003 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Brendan Kehoe (brendan@zen.org). + + 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. */ + +#ifdef __ASSEMBLER__ + +#include <sgidefs.h> +#include <sys/regdef.h> + +#define ENTRY(name) \ + .globl name; \ + .align 2; \ + .ent name,0; \ + name##: + +#undef END +#define END(function) \ + .end function; \ + .size function,.-function + +#if _MIPS_SIM == _MIPS_SIM_ABI32 || _MIPS_SIM == _MIPS_SIM_ABIO64 +# define L(label) $L ## label +#else +# define L(label) .L ## label +#endif + +#ifdef libc_hidden_builtin_def +#error WHOA!!! libc_hidden_builtin_def is defined +#else +#define libc_hidden_builtin_def(name) +#endif + +#endif |