feat(strings): strcasecmp/ffs/bcopy/bzero
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* vlibc — <strings.h> legacy functions (todo 10).
|
||||
*
|
||||
* The whole file self-gates at VLIBC_LEVEL >= 2 (the build wiring pass may
|
||||
* compile it unconditionally): at level 1 this translation unit produces
|
||||
* nothing, because every function here is XSI/BSD legacy and none is
|
||||
* POSIX.1-2008 base.
|
||||
*
|
||||
* bcopy/bzero/bcmp delegate to the project's own memmove/memset/memcmp
|
||||
* (satisfied from the archive at link time) — bcopy MUST NOT be a bare
|
||||
* memcpy alias, because its contract includes overlapping regions.
|
||||
* index/rindex are written as direct byte scans (own code, not strchr
|
||||
* wrappers) so their documented unsigned char conversion is explicit.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Fold an ASCII uppercase letter to lowercase; every other byte passes
|
||||
* through unchanged. Kept local and locale-free: the BSD comparisons must
|
||||
* never depend on ctype tables or the active locale.
|
||||
*/
|
||||
static inline unsigned char
|
||||
fold_ascii_byte(unsigned char c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
{
|
||||
return (unsigned char)(c + ('a' - 'A'));
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare s1 and s2 byte-wise ignoring case. Equal-folded runs are skipped
|
||||
* with a raw-byte fast path; the first differing byte pair is compared
|
||||
* folded, so "A" and "a" order as equal and the returned sign is that of
|
||||
* the folded difference. The scan stops at the first NUL in either string.
|
||||
*/
|
||||
int
|
||||
strcasecmp(const char *s1, const char *s2)
|
||||
{
|
||||
const unsigned char *l = (const unsigned char *)s1;
|
||||
const unsigned char *r = (const unsigned char *)s2;
|
||||
|
||||
for (; *l == *r; l++, r++)
|
||||
{
|
||||
if (*l == '\0')
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return (int)fold_ascii_byte(*l) - (int)fold_ascii_byte(*r);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare at most n bytes of s1 and s2 as strcasecmp does, stopping early
|
||||
* at the first folded difference or the first NUL. When the comparison
|
||||
* ends at a NUL, the shorter (equal-prefix) string sorts first.
|
||||
*/
|
||||
int
|
||||
strncasecmp(const char *s1, const char *s2, size_t n)
|
||||
{
|
||||
const unsigned char *l = (const unsigned char *)s1;
|
||||
const unsigned char *r = (const unsigned char *)s2;
|
||||
|
||||
while (n != 0 && *l != '\0' && *r != '\0')
|
||||
{
|
||||
const unsigned char fl = fold_ascii_byte(*l);
|
||||
const unsigned char fr = fold_ascii_byte(*r);
|
||||
|
||||
if (fl != fr)
|
||||
{
|
||||
return (int)fl - (int)fr;
|
||||
}
|
||||
l++;
|
||||
r++;
|
||||
n--;
|
||||
}
|
||||
|
||||
/* n bytes compared: equal so far. */
|
||||
if (n == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* A NUL ended the scan: the one that is NUL here is the shorter. */
|
||||
return (int)*l - (int)*r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the 1-based index of the least significant set bit of i, or 0
|
||||
* when i is 0. The zero case is guarded because __builtin_ctz is
|
||||
* undefined for a zero argument.
|
||||
*/
|
||||
int
|
||||
ffs(int i)
|
||||
{
|
||||
return i == 0 ? 0 : __builtin_ctz((unsigned int)i) + 1;
|
||||
}
|
||||
|
||||
int
|
||||
ffsl(long i)
|
||||
{
|
||||
return i == 0 ? 0 : __builtin_ctzl((unsigned long)i) + 1;
|
||||
}
|
||||
|
||||
int
|
||||
ffsll(long long i)
|
||||
{
|
||||
return i == 0 ? 0 : __builtin_ctzll((unsigned long long)i) + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy n bytes from src to dst with memmove semantics: the regions may
|
||||
* overlap and the copy behaves as if through a temporary. The argument
|
||||
* order (src, dst) is the reverse of memmove's — kept as such because it
|
||||
* is the BSD contract.
|
||||
*/
|
||||
void
|
||||
bcopy(const void *src, void *dst, size_t n)
|
||||
{
|
||||
(void)memmove(dst, src, n);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill n bytes at s with zero.
|
||||
*/
|
||||
void
|
||||
bzero(void *s, size_t n)
|
||||
{
|
||||
(void)memset(s, 0, n);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare the first n bytes of s1 and s2 as unsigned char, like memcmp;
|
||||
* return 0 when equal, nonzero otherwise. A NUL byte does not end the
|
||||
* comparison.
|
||||
*/
|
||||
int
|
||||
bcmp(const void *s1, const void *s2, size_t n)
|
||||
{
|
||||
return memcmp(s1, s2, n);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a pointer to the first occurrence of c (converted to unsigned
|
||||
* char) in s, or NULL when absent. The terminating NUL is part of the
|
||||
* string, so index(s, '\0') returns a pointer to it. Legacy name for
|
||||
* strchr.
|
||||
*/
|
||||
char *
|
||||
index(const char *s, int c)
|
||||
{
|
||||
const unsigned char cc = (unsigned char)c;
|
||||
|
||||
for (;; s++)
|
||||
{
|
||||
if ((unsigned char)*s == cc)
|
||||
{
|
||||
return (char *)s;
|
||||
}
|
||||
if (*s == '\0')
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a pointer to the last occurrence of c (converted to unsigned
|
||||
* char) in s, or NULL when absent. The terminating NUL is part of the
|
||||
* string, so rindex(s, '\0') returns a pointer to it. Legacy name for
|
||||
* strrchr.
|
||||
*/
|
||||
char *
|
||||
rindex(const char *s, int c)
|
||||
{
|
||||
const unsigned char cc = (unsigned char)c;
|
||||
const char *last = NULL;
|
||||
|
||||
for (;; s++)
|
||||
{
|
||||
if ((unsigned char)*s == cc)
|
||||
{
|
||||
last = s;
|
||||
}
|
||||
if (*s == '\0')
|
||||
{
|
||||
return (char *)last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
Reference in New Issue
Block a user