Skip to content
File

Blob: src/workerd/util/strings.c++

3.5 KB
1#include "strings.h"
2 
3namespace workerd {
4namespace {
5constexpr uint64_t broadcast(uint8_t v) noexcept {
6 return 0x101010101010101ull * v;
7}
8 
9// SWAR routine designed to convert ASCII uppercase letters to lowercase.
10// Let's process 8 bytes (64 bits) at a time using a single 64-bit int,
11// treating as 8 parallel 8-bit values.
12// PS: This will enable the use of auto-vectorization.
13constexpr void toLowerAscii(char* input, size_t length) noexcept {
14 constexpr const uint64_t broadcast_80 = broadcast(0x80);
15 constexpr const uint64_t broadcast_Ap = broadcast(128 - 'A');
16 constexpr const uint64_t broadcast_Zp = broadcast(128 - 'Z' - 1);
17 size_t i = 0;
18 for (; i + 7 < length; i += 8) {
19 uint64_t word{};
20 memcpy(&word, input + i, sizeof(word));
21 word ^= (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
22 memcpy(input + i, &word, sizeof(word));
23 }
24 if (i < length) {
25 uint64_t word{};
26 memcpy(&word, input + i, length - i);
27 word ^= (((word + broadcast_Ap) ^ (word + broadcast_Zp)) & broadcast_80) >> 2;
28 memcpy(input + i, &word, length - i);
29 }
30}
31 
32constexpr void toUpperAscii(char* input, size_t length) noexcept {
33 constexpr const uint64_t broadcast_80 = broadcast(0x80);
34 constexpr const uint64_t broadcast_ap = broadcast(128 - 'a');
35 constexpr const uint64_t broadcast_zp = broadcast(128 - 'z' - 1);
36 size_t i = 0;
37 for (; i + 7 < length; i += 8) {
38 uint64_t word{};
39 memcpy(&word, input + i, sizeof(word));
40 word ^= (((word + broadcast_ap) ^ (word + broadcast_zp)) & broadcast_80) >> 2;
41 memcpy(input + i, &word, sizeof(word));
42 }
43 if (i < length) {
44 uint64_t word{};
45 memcpy(&word, input + i, length - i);
46 word ^= (((word + broadcast_ap) ^ (word + broadcast_zp)) & broadcast_80) >> 2;
47 memcpy(input + i, &word, length - i);
48 }
49}
50} // namespace
51 
52kj::String toLower(kj::String&& str) {
53 toLowerAscii(str.begin(), str.size());
54 return kj::mv(str);
55}
56 
57kj::String toUpper(kj::String&& str) {
58 toUpperAscii(str.begin(), str.size());
59 return kj::mv(str);
60}
61 
62kj::String toLower(kj::ArrayPtr<const char> ptr) {
63 return toLower(kj::str(ptr));
64}
65 
66kj::String toUpper(kj::ArrayPtr<const char> ptr) {
67 return toUpper(kj::str(ptr));
68}
69 
70kj::ArrayPtr<const char> trimLeadingAndTrailingWhitespace(kj::ArrayPtr<const char> ptr) {
71 size_t start = 0;
72 auto end = ptr.size();
73 while (start < end && isAsciiWhitespace(ptr[start])) {
74 start++;
75 }
76 while (end > start && isAsciiWhitespace(ptr[end - 1])) {
77 end--;
78 }
79 return ptr.slice(start, end).asChars();
80}
81 
82kj::ArrayPtr<const char> trimTailingWhitespace(kj::ArrayPtr<const char> ptr) {
83 auto end = ptr.size();
84 while (end > 0 && isAsciiWhitespace(ptr[end - 1])) {
85 end--;
86 }
87 return ptr.first(end).asChars();
88}
89 
90kj::Array<kj::byte> stripInnerWhitespace(kj::ArrayPtr<kj::byte> input) {
91 auto result = kj::heapArray<kj::byte>(input.size());
92 size_t len = 0;
93 for (const kj::byte c: input) {
94 if (!isAsciiWhitespace(c)) {
95 result[len++] = c;
96 }
97 }
98 return result.first(len).attach(kj::mv(result));
99};
100 
101bool strcaseeq(kj::ArrayPtr<const char> a, kj::ArrayPtr<const char> b) {
102 // This could likely be optimized further but this is more than sufficient for now.
103 if (a.size() != b.size()) return false;
104 for (size_t i = 0; i < a.size(); ++i) {
105 char ca = a[i];
106 char cb = b[i];
107 // Convert to lowercase for comparison
108 if (isAlphaUpper(ca)) ca += 32;
109 if (isAlphaUpper(cb)) cb += 32;
110 if (ca != cb) return false;
111 }
112 return true;
113}
114 
115} // namespace workerd