File
Blob: src/workerd/util/strings.c++
| 1 | #include "strings.h" |
| 2 | |
| 3 | namespace workerd { |
| 4 | namespace { |
| 5 | constexpr 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. |
| 13 | constexpr 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 | |
| 32 | constexpr 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 | |
| 52 | kj::String toLower(kj::String&& str) { |
| 53 | toLowerAscii(str.begin(), str.size()); |
| 54 | return kj::mv(str); |
| 55 | } |
| 56 | |
| 57 | kj::String toUpper(kj::String&& str) { |
| 58 | toUpperAscii(str.begin(), str.size()); |
| 59 | return kj::mv(str); |
| 60 | } |
| 61 | |
| 62 | kj::String toLower(kj::ArrayPtr<const char> ptr) { |
| 63 | return toLower(kj::str(ptr)); |
| 64 | } |
| 65 | |
| 66 | kj::String toUpper(kj::ArrayPtr<const char> ptr) { |
| 67 | return toUpper(kj::str(ptr)); |
| 68 | } |
| 69 | |
| 70 | kj::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 | |
| 82 | kj::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 | |
| 90 | kj::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 | |
| 101 | bool 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 |