blob: 482bf3e63a8a519a662dae2962856c890f3a486c [file]
/*
* Copyright (C) 2017 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <string.h>
#include <xlocale.h>
//
// Core functionality.
//
#if !defined(__x86_64__)
void* memccpy(void* dst, const void* src, int c, size_t n) {
const char* p = static_cast<const char*>(memchr(src, c, n));
if (p != nullptr) {
n = p - static_cast<const char*>(src) + 1;
memcpy(dst, src, n);
return static_cast<char*>(dst) + n;
}
memcpy(dst, src, n);
return nullptr;
}
#endif
void* mempcpy(void* dst, const void* src, size_t n) {
return reinterpret_cast<char*>(memcpy(dst, src, n)) + n;
}
// https://github.com/ARM-software/optimized-routines/issues/89
#if defined(__aarch64__)
char* strcat(char* dst, const char* src) {
strcpy(dst + strlen(dst), src);
return dst;
}
#endif
#if defined(__arm__) || defined(__aarch64__) || defined(__riscv)
__attribute__((__flatten__))
char* stpncpy(char* dst, const char* src, size_t dst_n) {
size_t src_n = strnlen(src, dst_n);
memcpy(dst, src, src_n);
memset(dst + src_n, 0, dst_n - src_n);
return dst + src_n;
}
#endif
#if defined(__arm__) || defined(__aarch64__)
__attribute__((__flatten__))
char* strncpy(char* dst, const char* src, size_t n) {
stpncpy(dst, src, n);
return dst;
}
#endif
// If you have a fast memchr(), but not a strnlen().
#if defined(__x86_64__)
size_t strnlen(const char* s, size_t n) {
const char* nul = static_cast<const char*>(memchr(s, '\0', n));
return nul ? (nul - s) : n;
}
#endif
//
// String delimiter functions.
//
// The approach here is to optimize strcspn()/strspn() and write everything
// else in terms of those two.
// Benchmarking shows that bool[] works better than a bitset,
// and 256 bytes of stack (the latter half of which is never used in practice)
// doesn't seem unreasonable.
static_assert(sizeof(bool) == 1);
static inline void init_delimiter_set(bool* set, const char* delims) {
for (const uint8_t* d = reinterpret_cast<const uint8_t*>(delims); *d; ++d) {
set[*d] = true;
}
}
__attribute__((__flatten__))
size_t strspn(const char* ss, const char* delims) {
const uint8_t* s = reinterpret_cast<const uint8_t*>(ss);
const uint8_t* p = s;
if (delims[0] == '\0') return 0;
if (delims[1] == '\0') {
// The common case is a single delimiter, where the set is worse.
while (*p == delims[0]) ++p;
} else {
bool set[256] = {};
init_delimiter_set(set, delims);
while (set[*p]) ++p;
}
return p - s;
}
__attribute__((__flatten__))
size_t strcspn(const char* ss, const char* delims) {
const uint8_t* s = reinterpret_cast<const uint8_t*>(ss);
const uint8_t* p = s;
if (delims[0] == '\0' || delims[1] == '\0') {
// The common case is a single delimiter, where the set is far worse.
// On arm64 strchrnul() is faster than open coding even for small distances,
// and orders of magnitude better for large distances.
return strchrnul(ss, delims[0]) - ss;
} else {
bool set[256] = {};
init_delimiter_set(set, delims);
while (*p && !set[*p]) ++p;
}
return p - s;
}
__attribute__((__flatten__))
char* strpbrk(const char* s, const char* delims) {
size_t i = strcspn(s, delims);
if (s[i] != '\0') return const_cast<char*>(s) + i;
return nullptr;
}
__attribute__((__flatten__))
char* strsep(char** last, const char* delims) {
// Already finished?
char* start = *last;
if (start == nullptr) return nullptr;
// Find end of token (first delimiter).
char* end = start + strcspn(start, delims);
if (*end != '\0') {
// Replace delimiter with NUL, point past it.
*end = '\0';
*last = end + 1;
} else {
// Token ends at end of string.
// Signal that to next invocation.
*last = nullptr;
}
// Return pointer to start of token.
return start;
}
__attribute__((__flatten__))
char* strtok_r(char* s, const char* delims, char** last) {
// Already finished?
if (s == nullptr && (s = *last) == nullptr) {
return nullptr;
}
// Skip leading delimiters with optimized strspn().
s += strspn(s, delims);
// Finished now (no non-delimiters)?
if (*s == '\0') {
*last = nullptr;
return nullptr;
}
// Find next delimiter with optimized strcspn().
*last = s + strcspn(s, delims);
if (**last != '\0') {
// Replace delimiter with NUL, point past it.
**last = '\0';
*last += 1;
} else {
// Token ends at end of string.
// Signal that to next invocation.
*last = nullptr;
}
// Return pointer to start of token.
return s;
}
__attribute__((__flatten__))
char* strtok(char* s, const char* delims) {
static char* last;
return strtok_r(s, delims, &last);
}
//
// No-op i18n stuff.
//
int strcoll(const char* lhs, const char* rhs) {
return strcmp(lhs, rhs);
}
__strong_alias(strcoll_l, strcoll);
size_t strxfrm(char* dst, const char* src, size_t n) {
return strlcpy(dst, src, n);
}
__strong_alias(strxfrm_l, strxfrm);