Skip to content

Commit f98e975

Browse files
committed
using stricmp_fast() in estring_view::istarts_with() and estring_view::icmp()
1 parent c2baa6f commit f98e975

3 files changed

Lines changed: 126 additions & 77 deletions

File tree

common/estring.cpp

Lines changed: 64 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -161,44 +161,79 @@ static_assert(
161161

162162
namespace photon {
163163

164-
void tolower_fast(char* out, const char* in, size_t len) {
165-
size_t i = 0;
166-
for (; i < len/8*8; i+=8)
167-
*(uint64_t*)&out[i] = tolower_fast8(*(uint64_t*)&in[i]);
168-
for (; i < len; i++)
169-
out[i] = tolower_fast(in[i]);
170-
out[len] = '\0';
164+
inline uint64_t check_cases(uint64_t x, char a, char z) {
165+
uint64_t all_bytes = 0x0101010101010101;
166+
uint64_t heptets = x & (0x7f * all_bytes);
167+
uint64_t is_ascii = ~x & (0x80 * all_bytes);
168+
uint64_t is_gt_Z = heptets + (0x7f - z) * all_bytes;
169+
uint64_t is_ge_A = heptets + (0x80 - a) * all_bytes;
170+
return (is_ge_A ^ is_gt_Z) & is_ascii;
171171
}
172172

173-
void toupper_fast(char* out, const char* in, size_t len) {
174-
size_t i = 0;
175-
for (; i < len/8*8; i += 8)
176-
*(uint64_t*)&out[i] = toupper_fast8(*(uint64_t*)&in[i]);
177-
for (;i < len; i++)
178-
out[i] = toupper_fast(in[i]);
173+
inline uint64_t tolower_fast8(uint64_t x) {
174+
uint64_t is_upper = check_cases(x, 'A', 'X');
175+
return x | (is_upper >> 2);
176+
}
177+
178+
inline uint64_t toupper_fast8(uint64_t x) {
179+
uint64_t is_lower = check_cases(x, 'a', 'z');
180+
return x ^ (is_lower >> 2);
181+
}
182+
183+
template<typename F1, typename F8> inline
184+
void convert_case(char* out, const char* in, size_t len, F1 f1, F8 f8) {
185+
if (unlikely(len == 0))
186+
len = strlen(in);
187+
if (unlikely(len < 8)) {
188+
for (size_t i = 0; i < len; i++)
189+
out[i] = f1(in[i]);
190+
} else {
191+
for (size_t i = 0; i < len/8*8; i+=8)
192+
*(uint64_t*)&out[i] = f8(*(uint64_t*)&in[i]);
193+
*(uint64_t*)&out[len-8] = f8(*(uint64_t*)&in[len-8]);
194+
}
179195
out[len] = '\0';
180196
}
181197

198+
void tolower_fast(char* out, const char* in, size_t len) {
199+
convert_case(out, in, len,
200+
[](char x) { return tolower_fast(x); },
201+
[](uint64_t x) { return tolower_fast8(x); });
202+
}
203+
204+
void toupper_fast(char* out, const char* in, size_t len) {
205+
convert_case(out, in, len,
206+
[](char x) { return toupper_fast(x); },
207+
[](uint64_t x) { return toupper_fast8(x); });
208+
}
182209

210+
inline uint64_t icmp8(const char* a, const char* b) {
211+
auto ca = tolower_fast8(*(uint64_t*)a);
212+
auto cb = tolower_fast8(*(uint64_t*)b);
213+
return __builtin_bswap64(ca) - __builtin_bswap64(cb);
214+
}
215+
inline int spaceship(uint64_t x) {
216+
uint32_t high = x >> 32, low = x & 0xffffffff;
217+
return int(high | (low >> 1) | (low & 1));
218+
}
183219
int stricmp_fast(std::string_view a, std::string_view b) {
184-
size_t i = 0, min = std::min(a.size(), b.size());
185-
for (; i < min/8*8; i+=8) {
186-
auto ca = tolower_fast8(*(uint64_t*)&a[i]);
187-
auto cb = tolower_fast8(*(uint64_t*)&b[i]);
188-
if (ca == cb) continue;
189-
auto c = ca - cb;
190-
for (; c; c>>=8) {
191-
auto delta = (char)(c & 0xff);
192-
if (delta) return delta;
220+
size_t i = 0, len = std::min(a.size(), b.size());
221+
if (unlikely(len < 8)) {
222+
for (; i < len; i++) {
223+
auto ca = tolower_fast(a[i]);
224+
auto cb = tolower_fast(b[i]);
225+
if (auto x = ca - cb)
226+
return x;
193227
}
228+
return spaceship(a.size() - b.size());
194229
}
195-
for (; i < min; i++) {
196-
auto ca = tolower_fast(a[i]);
197-
auto cb = tolower_fast(b[i]);
198-
auto delta = ca - cb;
199-
if (delta) return delta;
200-
}
201-
return int(a.size() - b.size());
230+
for (; i < len/8*8; i+=8)
231+
if (auto x = icmp8(&a[i], &b[i]))
232+
return spaceship(x);
233+
if (likely(i < len)) // len >= 8
234+
if (auto x = icmp8(&a[len-8], &b[len-8]))
235+
return spaceship(x);
236+
return spaceship(a.size() - b.size());
202237
}
203238

204239
}

common/estring.h

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ struct charset : std::bitset<256>
5656
// }
5757
};
5858

59+
class estring;
5960
class estring_view : public std::string_view
6061
{
6162
public:
@@ -141,30 +142,23 @@ class estring_view : public std::string_view
141142
return substr(start, end - start + 1);
142143
}
143144
#if __cplusplus < 202000L
144-
bool starts_with(estring_view x)
145-
{
145+
bool starts_with(std::string_view x) const {
146146
auto len = x.size();
147147
return length() >= len &&
148148
memcmp(data(), x.data(), len) == 0;
149149
}
150150

151-
bool ends_with(estring_view x)
152-
{
151+
bool ends_with(std::string_view x) const {
153152
auto len = x.size();
154153
return length() >= len &&
155154
memcmp(&*end() - len, x.data(), len) == 0;
156155
}
157156
#endif
158157

159-
bool istarts_with(estring_view x) {
160-
return strncasecmp(data(), x.data(), x.size()) == 0;
161-
}
162-
163-
int icmp(estring_view x) {
164-
auto ret = strncasecmp(data(), x.data(), std::min(size(), x.size()));
165-
if (ret != 0) return ret;
166-
return size() - x.size();
167-
}
158+
bool istarts_with(std::string_view x) const;
159+
int icmp(std::string_view x) const;
160+
estring tolower_fast() const;
161+
estring toupper_fast() const;
168162

169163
template<typename Separator>
170164
struct _split
@@ -461,7 +455,6 @@ class estring : public std::string
461455

462456
estring() = default;
463457
estring(const std::string& v) : std::string(v) {}
464-
estring(estring_view sv) : std::string(sv) { }
465458
estring(std::string_view sv) : std::string(sv) { }
466459

467460
estring_view view() const
@@ -473,12 +466,10 @@ class estring : public std::string
473466
return view().trim(spaces);
474467
}
475468
#if __cplusplus < 202000L
476-
bool starts_with(estring_view x)
477-
{
469+
bool starts_with(std::string_view x) const {
478470
return view().starts_with(x);
479471
}
480-
bool ends_with(estring_view x)
481-
{
472+
bool ends_with(std::string_view x) const {
482473
return view().ends_with(x);
483474
}
484475
#endif
@@ -705,32 +696,44 @@ inline char toupper_fast(char c) {
705696
return c - ('a' - 'A') * ('a' <= c && c <= 'z');
706697
}
707698

708-
inline uint64_t tolower_fast8(uint64_t x) {
709-
uint64_t all_bytes = 0x0101010101010101;
710-
uint64_t heptets = x & (0x7f * all_bytes);
711-
uint64_t is_ascii = ~x & (0x80 * all_bytes);
712-
uint64_t is_gt_Z = heptets + (0x7f - 'Z') * all_bytes;
713-
uint64_t is_ge_A = heptets + (0x80 - 'A') * all_bytes;
714-
uint64_t is_upper = (is_ge_A ^ is_gt_Z) & is_ascii;
715-
return x | (is_upper >> 2);
716-
}
717-
718-
inline uint64_t toupper_fast8(uint64_t x) {
719-
uint64_t all_bytes = 0x0101010101010101;
720-
uint64_t heptets = x & (0x7f * all_bytes);
721-
uint64_t is_ascii = ~x & (0x80 * all_bytes);
722-
uint64_t is_gt_z = heptets + (0x7f - 'z') * all_bytes;
723-
uint64_t is_ge_a = heptets + (0x80 - 'a') * all_bytes;
724-
uint64_t is_lower = (is_ge_a ^ is_gt_z) & is_ascii;
725-
return x ^ (is_lower >> 2);
726-
}
727-
728-
// convert string to lower or upper, the storage of out must be >= len + 1
699+
// convert string to lower or upper, if len == 0 then len = strlen(in)
700+
// the storage of out must be >= len + 1
729701
// it's possible that out == in
730702
void tolower_fast(char* out, const char* in, size_t len);
731703
void toupper_fast(char* out, const char* in, size_t len);
704+
inline void tolower_fast(char* out, std::string_view in) {
705+
tolower_fast(out, in.data(), in.size());
706+
}
707+
inline void toupper_fast(char* out, std::string_view in) {
708+
toupper_fast(out, in.data(), in.size());
709+
}
710+
inline estring tolower_fast(std::string_view in) {
711+
estring out(in.size(), '\0');
712+
tolower_fast(&out[0], in);
713+
return out;
714+
}
715+
inline estring toupper_fast(std::string_view in) {
716+
estring out(in.size(), '\0');
717+
toupper_fast(&out[0], in);
718+
return out;
719+
}
732720

733-
// compare 2 strings without case sensitive
721+
// compare 2 strings ignoring cases
734722
int stricmp_fast(std::string_view a, std::string_view b);
735723

736724
} // namespace photon
725+
726+
inline bool estring_view::istarts_with(std::string_view x) const {
727+
return size() >= x.size() && photon::stricmp_fast(*this, x) == 0;
728+
}
729+
730+
inline int estring_view::icmp(std::string_view x) const {
731+
return photon::stricmp_fast(*this, x);
732+
}
733+
734+
inline estring estring_view::tolower_fast() const {
735+
return photon::toupper_fast(*this);
736+
}
737+
inline estring estring_view::toupper_fast() const {
738+
return photon::toupper_fast(*this);
739+
}

common/test/test.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,25 +1282,36 @@ TEST(tolowerupper, basic) {
12821282
EXPECT_EQ(toupper_fast('3'), '3');
12831283
EXPECT_EQ(toupper_fast('z'), 'Z');
12841284

1285-
const static char s1[]="abC1dEf2%^&", s2[]="ABc1DeF2%^&";
1286-
EXPECT_EQ(toupper_fast8(*(uint64_t*)s1),
1287-
toupper_fast8(*(uint64_t*)s2));
1288-
EXPECT_EQ(tolower_fast8(*(uint64_t*)s1),
1289-
tolower_fast8(*(uint64_t*)s2));
1290-
EXPECT_EQ(stricmp_fast(string_view(s1), string_view(s2)), 0);
1285+
EXPECT_LT(stricmp_fast("abCd", "ABcD2"), 0);
1286+
EXPECT_LT(stricmp_fast("abCd1", "ABcD2"), 0);
1287+
EXPECT_EQ(stricmp_fast("abC1dEf2%^&", "ABc1DeF2%^&"), 0);
1288+
EXPECT_GT(stricmp_fast("xxxxxxxxxxxjkl;", "xxxxxxxxxxxJKL:"), 0);
1289+
EXPECT_LT(stricmp_fast("xxxxxxxxxxxaccc", "xxxxxxxxxxxBBBB"), 0);
1290+
1291+
auto sign = [](int x) { return (x > 0) ? 1 :
1292+
(x < 0 ? -1 : 0);
1293+
};
1294+
const char* headers[] = {"Host", "Content-Length", "Range", "User-Agent", "Connection"};
1295+
for (auto h1: headers)
1296+
for (auto h2: headers) {
1297+
if (sign(stricmp_fast(h1, h2)) != sign(strcasecmp(h1, h2))) {
1298+
LOG_DEBUG(VALUE(h1), VALUE(h2));
1299+
EXPECT_EQ(sign(stricmp_fast(h1, h2)), sign(strcasecmp(h1, h2)));
1300+
}
1301+
}
12911302
}
12921303

12931304
const static char S1[]="ABCDEFGHIJKLMNOPQRSTUVWXYZ",
12941305
S2[]="abcdefghijklmnopqrstuvwxyz";
12951306
TEST(tolowerupper, perf_strncasecmp) {
1296-
for (int i = 1000000; i; --i) {
1307+
for (int i = 10000000; i; --i) {
12971308
auto ret = strncasecmp(S1, S2, LEN(S1) - 1);
12981309
asm volatile(""::"r"(ret));
12991310
}
13001311
}
13011312

13021313
TEST(tolowerupper, perf_photon_stricmp) {
1303-
for (int i = 1000000; i; --i) {
1314+
for (int i = 10000000; i; --i) {
13041315
auto ret = stricmp_fast(S1, S2);
13051316
asm volatile(""::"r"(ret));
13061317
}

0 commit comments

Comments
 (0)