#pragma once #include <util/system/defaults.h> #include <util/system/compat.h> #include <util/generic/string.h> // ctype.h-like functions, locale-independent: // IsAscii{Upper,Lower,Digit,Alpha,Alnum,Space} and // AsciiTo{Upper,Lower} // // standard functions from <ctype.h> are locale dependent, // and cause undefined behavior when called on chars outside [0..127] range namespace NPrivate { enum ECharClass { CC_SPACE = 1, CC_UPPER = 2, CC_LOWER = 4, CC_DIGIT = 8, CC_ALPHA = 16, CC_ALNUM = 32, CC_ISHEX = 64, CC_PUNCT = 128, }; extern const unsigned char ASCII_CLASS[256]; extern const unsigned char ASCII_LOWER[256]; template <class T> struct TDereference { using type = T; }; #ifndef TSTRING_IS_STD_STRING template <class String> struct TDereference<TBasicCharRef<String>> { using type = typename String::value_type; }; #endif template <class T> using TDereferenced = typename TDereference<T>::type; template <class T> bool RangeOk(T c) noexcept { static_assert(std::is_integral<T>::value, "Integral type character expected"); if (sizeof(T) == 1) { return true; } return c >= static_cast<T>(0) && c <= static_cast<T>(127); } #ifndef TSTRING_IS_STD_STRING template <class String> bool RangeOk(const TBasicCharRef<String>& c) { return RangeOk(static_cast<typename String::value_type>(c)); } #endif } constexpr bool IsAscii(const int c) noexcept { return !(c & ~0x7f); } inline bool IsAsciiSpace(unsigned char c) { return ::NPrivate::ASCII_CLASS[c] & ::NPrivate::CC_SPACE; } inline bool IsAsciiUpper(unsigned char c) { return ::NPrivate::ASCII_CLASS[c] & ::NPrivate::CC_UPPER; } inline bool IsAsciiLower(unsigned char c) { return ::NPrivate::ASCII_CLASS[c] & ::NPrivate::CC_LOWER; } inline bool IsAsciiDigit(unsigned char c) { return ::NPrivate::ASCII_CLASS[c] & ::NPrivate::CC_DIGIT; } inline bool IsAsciiAlpha(unsigned char c) { return ::NPrivate::ASCII_CLASS[c] & ::NPrivate::CC_ALPHA; } inline bool IsAsciiAlnum(unsigned char c) { return ::NPrivate::ASCII_CLASS[c] & ::NPrivate::CC_ALNUM; } inline bool IsAsciiHex(unsigned char c) { return ::NPrivate::ASCII_CLASS[c] & ::NPrivate::CC_ISHEX; } inline bool IsAsciiPunct(unsigned char c) { return ::NPrivate::ASCII_CLASS[c] & ::NPrivate::CC_PUNCT; } // some overloads template <class T> inline bool IsAsciiSpace(T c) { return ::NPrivate::RangeOk(c) && IsAsciiSpace(static_cast<unsigned char>(c)); } template <class T> inline bool IsAsciiUpper(T c) { return ::NPrivate::RangeOk(c) && IsAsciiUpper(static_cast<unsigned char>(c)); } template <class T> inline bool IsAsciiLower(T c) { return ::NPrivate::RangeOk(c) && IsAsciiLower(static_cast<unsigned char>(c)); } template <class T> inline bool IsAsciiDigit(T c) { return ::NPrivate::RangeOk(c) && IsAsciiDigit(static_cast<unsigned char>(c)); } template <class T> inline bool IsAsciiAlpha(T c) { return ::NPrivate::RangeOk(c) && IsAsciiAlpha(static_cast<unsigned char>(c)); } template <class T> inline bool IsAsciiAlnum(T c) { return ::NPrivate::RangeOk(c) && IsAsciiAlnum(static_cast<unsigned char>(c)); } template <class T> inline bool IsAsciiHex(T c) { return ::NPrivate::RangeOk(c) && IsAsciiHex(static_cast<unsigned char>(c)); } template <class T> inline bool IsAsciiPunct(T c) { return ::NPrivate::RangeOk(c) && IsAsciiPunct(static_cast<unsigned char>(c)); } // some extra helpers inline ui8 AsciiToLower(ui8 c) noexcept { return ::NPrivate::ASCII_LOWER[c]; } inline char AsciiToLower(char c) noexcept { return (char)AsciiToLower((ui8)c); } template <class T> inline ::NPrivate::TDereferenced<T> AsciiToLower(T c) noexcept { return (c >= 0 && c <= 127) ? (::NPrivate::TDereferenced<T>)AsciiToLower((ui8)c) : c; } template <class T> inline ::NPrivate::TDereferenced<T> AsciiToUpper(T c) noexcept { return IsAsciiLower(c) ? (c + ('A' - 'a')) : c; } /** * ASCII case-insensitive string comparison (for proper UTF8 strings * case-insensitive comparison consider using @c library/cpp/charset). * * BUGS: Currently will NOT work properly with strings that contain * 0-terminator character inside. See IGNIETFERRO-1641 for details. * * @return true iff @c s1 ans @c s2 are case-insensitively equal. */ static inline bool AsciiEqualsIgnoreCase(const char* s1, const char* s2) noexcept { return stricmp(s1, s2) == 0; } /** * ASCII case-insensitive string comparison (for proper UTF8 strings * case-insensitive comparison consider using @c library/cpp/charset). * * BUGS: Currently will NOT work properly with strings that contain * 0-terminator character inside. See IGNIETFERRO-1641 for details. * * @return true iff @c s1 ans @c s2 are case-insensitively equal. */ static inline bool AsciiEqualsIgnoreCase(const TStringBuf s1, const TStringBuf s2) noexcept { return (s1.size() == s2.size()) && strnicmp(s1.data(), s2.data(), s1.size()) == 0; } /** * ASCII case-insensitive string comparison (for proper UTF8 strings * case-insensitive comparison consider using @c library/cpp/charset). * * BUGS: Currently will NOT work properly with strings that contain * 0-terminator character inside. See IGNIETFERRO-1641 for details. * * @return 0 if strings are equal, negative if @c s1 < @c s2 * and positive otherwise. * (same value as @c stricmp does). */ static inline int AsciiCompareIgnoreCase(const char* s1, const char* s2) noexcept { return stricmp(s1, s2); } /** * ASCII case-insensitive string comparison (for proper UTF8 strings * case-insensitive comparison consider using @c library/cpp/charset). * * BUGS: Currently will NOT work properly with strings that contain * 0-terminator character inside. See IGNIETFERRO-1641 for details. * * @return * - zero if strings are equal * - negative if @c s1 < @c s2 * - positive otherwise, * similar to stricmp. */ Y_PURE_FUNCTION int AsciiCompareIgnoreCase(const TStringBuf s1, const TStringBuf s2) noexcept; /** * ASCII case-sensitive string comparison (for proper UTF8 strings * case-sensitive comparison consider using @c library/cpp/charset). * * BUGS: Currently will NOT work properly with strings that contain * 0-terminator character inside. See IGNIETFERRO-1641 for details. * * @return true iff @c s2 are case-sensitively prefix of @c s1. */ static inline bool AsciiHasPrefix(const TStringBuf s1, const TStringBuf s2) noexcept { return (s1.size() >= s2.size()) && memcmp(s1.data(), s2.data(), s2.size()) == 0; } /** * ASCII case-insensitive string comparison (for proper UTF8 strings * case-insensitive comparison consider using @c library/cpp/charset). * * @return true iff @c s2 are case-insensitively prefix of @c s1. */ static inline bool AsciiHasPrefixIgnoreCase(const TStringBuf s1, const TStringBuf s2) noexcept { return (s1.size() >= s2.size()) && strnicmp(s1.data(), s2.data(), s2.size()) == 0; } /** * ASCII case-insensitive string comparison (for proper UTF8 strings * case-insensitive comparison consider using @c library/cpp/charset). * * @return true iff @c s2 are case-insensitively suffix of @c s1. */ static inline bool AsciiHasSuffixIgnoreCase(const TStringBuf s1, const TStringBuf s2) noexcept { return (s1.size() >= s2.size()) && strnicmp((s1.data() + (s1.size() - s2.size())), s2.data(), s2.size()) == 0; }