| Daniel Cheng | 0f38d17 | 2024-12-16 19:39:43 | [diff] [blame] | 1 | // Copyright 2024 The Chromium Authors |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #ifndef BASE_STRINGS_STRING_SLICE_H_ |
| 6 | #define BASE_STRINGS_STRING_SLICE_H_ |
| 7 | |
| Takuto Ikuta | a1926f3 | 2025-01-07 19:06:18 | [diff] [blame] | 8 | #include <stddef.h> |
| Daniel Cheng | 0f38d17 | 2024-12-16 19:39:43 | [diff] [blame] | 9 | #include <stdint.h> |
| 10 | |
| 11 | #include <bit> |
| 12 | #include <string_view> |
| 13 | |
| 14 | namespace base::internal { |
| 15 | |
| 16 | // Determines the minimum unsigned integer type needed to hold `kSize`. |
| 17 | template <size_t kSize> |
| 18 | struct IndexTypeForSize { |
| 19 | private: |
| 20 | static constexpr auto Helper() { |
| 21 | constexpr int kMinBits = std::bit_width(kSize); |
| 22 | if constexpr (kMinBits <= 8) { |
| 23 | return uint8_t(); |
| 24 | } else if constexpr (kMinBits <= 16) { |
| 25 | return uint16_t(); |
| 26 | } else if constexpr (kMinBits <= 32) { |
| 27 | return uint32_t(); |
| 28 | } else { |
| 29 | return size_t(); |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | public: |
| 34 | using Type = decltype(Helper()); |
| 35 | }; |
| 36 | |
| 37 | } // namespace base::internal |
| 38 | |
| 39 | namespace base::subtle { |
| 40 | |
| 41 | // This is intended for use in tables generated by scripts and should not be |
| 42 | // directly used. Consider a global constant like this: |
| 43 | // |
| 44 | // constexpr std::string_view kNames[] = { |
| 45 | // "Alice", |
| 46 | // "Bob", |
| 47 | // "Eve", |
| 48 | // }; |
| 49 | // |
| 50 | // "Alice", "Bob", and "Eve" are also constants, but they are not stored inline |
| 51 | // in `kNames`; `kNames[0]` points to "Alice", `kNames[1]` points to "Bob", and |
| 52 | // `kNames[2]` points to "Eve". However, images can be loaded at arbitrary base |
| 53 | // addresses, so the actual pointer values are unknown at build time. |
| 54 | // |
| 55 | // To solve this, the tooling stores `kNames` in `.data.rel.ro` and records 3 |
| 56 | // relocations in `.rela.dyn`. When the image is loaded, the linker applies the |
| 57 | // relocations to fix up the addresses before marking the section read-only. |
| 58 | // |
| 59 | // Unfortunately, this has both a binary size cost (relocation entries are |
| 60 | // relatively large unless RELR is in use) and a runtime cost (to apply the |
| 61 | // relocations). |
| 62 | // |
| 63 | // StringSlice avoids relocations by only storing an offset and a length and |
| 64 | // dynamically resolving to a std::string_view at runtime. Using `StringSlice`, |
| 65 | // the above example might look like this instead: |
| 66 | // |
| Daniel Cheng | 236c754 | 2024-12-16 22:20:50 | [diff] [blame] | 67 | // constexpr char kData[] = "AliceBobEve"; |
| 68 | // constexpr StringSlice<sizeof(kData), kData> kNames[] = { |
| Daniel Cheng | 0f38d17 | 2024-12-16 19:39:43 | [diff] [blame] | 69 | // {0, 5}, |
| 70 | // {5, 3}, |
| 71 | // {8, 3}, |
| 72 | // }; |
| 73 | // |
| 74 | // While this has a small runtime cost (typically a PC-relative load), modern |
| 75 | // CPUs are quite good at this sort of math. |
| Daniel Cheng | 236c754 | 2024-12-16 22:20:50 | [diff] [blame] | 76 | template <size_t N, const char (&kData)[N]> |
| Daniel Cheng | 0f38d17 | 2024-12-16 19:39:43 | [diff] [blame] | 77 | struct StringSlice { |
| Daniel Cheng | 236c754 | 2024-12-16 22:20:50 | [diff] [blame] | 78 | using IndexType = typename internal::IndexTypeForSize<N>::Type; |
| Daniel Cheng | 0f38d17 | 2024-12-16 19:39:43 | [diff] [blame] | 79 | |
| 80 | IndexType offset; |
| 81 | IndexType length; |
| 82 | |
| 83 | friend constexpr bool operator==(StringSlice lhs, StringSlice rhs) { |
| 84 | return std::string_view(lhs) == std::string_view(rhs); |
| 85 | } |
| 86 | friend constexpr auto operator<=>(StringSlice lhs, StringSlice rhs) { |
| 87 | return std::string_view(lhs) <=> std::string_view(rhs); |
| 88 | } |
| 89 | constexpr operator std::string_view() const { |
| Daniel Cheng | 236c754 | 2024-12-16 22:20:50 | [diff] [blame] | 90 | // Note 1: using as_string_view() or span() can cause issues with constexpr |
| 91 | // evaluation limits. |
| 92 | // Note 2: Subtract 1 from kData since this is intended for use with string |
| 93 | // literals, and the terminating nul should not be included. |
| 94 | return std::string_view(kData, sizeof(kData) - 1).substr(offset, length); |
| Daniel Cheng | 0f38d17 | 2024-12-16 19:39:43 | [diff] [blame] | 95 | } |
| 96 | }; |
| 97 | |
| 98 | } // namespace base::subtle |
| 99 | |
| 100 | #endif // BASE_STRINGS_STRING_SLICE_H_ |