// FORTIFY only provides value when the size of the buffer can be // statically determined, and that's never the case with any calls // here. b/454067908 shows that leaving it enabled leads to // suboptimal codegen. #undef _FORTIFY_SOURCE
// The approach here is to optimize strcspn()/strspn() and write everything // else in terms of those two.
// arm64 and x86_64 have a psimd strspn. #ifdefined(__arm__) || defined(__i386__) || defined(__riscv) #define NEED_GENERIC_STRSPN 1 #else #define NEED_GENERIC_STRSPN 0 #endif
#if NEED_GENERIC_STRSPN // 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); staticinlinevoid init_delimiter_set(bool* set, constchar* delims) { for (const uint8_t* d = reinterpret_cast<const uint8_t*>(delims); *d; ++d) {
set[*d] = true;
}
} #endif
#if NEED_GENERIC_STRSPN
__attribute__((__flatten__))
size_t strspn(constchar* ss, constchar* delims) { const uint8_t* s = reinterpret_cast<const uint8_t*>(ss); const uint8_t* p = s; if (delims[0] == '\0') return0; 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;
} #endif
#if NEED_GENERIC_STRSPN
__attribute__((__flatten__))
size_t strcspn(constchar* ss, constchar* 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;
} #endif
__attribute__((__flatten__)) char* strsep(char** last, constchar* 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, constchar* 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;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.