Atneya Nair | ae31080 | 2022-08-15 15:30:31 -0700 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright (C) 2022 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #pragma once |
| 18 | |
| 19 | #include <string_view> |
| 20 | #include <type_traits> |
| 21 | |
| 22 | #pragma push_macro("EXPLICIT_CONVERSION_GENERATE_OPERATOR") |
| 23 | #undef EXPLICIT_CONVERSION_GENERATE_OPERATOR |
| 24 | #define EXPLICIT_CONVERSION_GENERATE_OPERATOR(T, U, op) \ |
| 25 | friend constexpr bool operator op(T lhs, T rhs) { \ |
| 26 | return operator op(static_cast<U>(lhs), static_cast<U>(rhs)); \ |
| 27 | } \ |
| 28 | friend constexpr bool operator op(T lhs, U rhs) { \ |
| 29 | return operator op(static_cast<U>(lhs), rhs); \ |
| 30 | } \ |
| 31 | friend constexpr bool operator op(U lhs, T rhs) { \ |
| 32 | return operator op(lhs, static_cast<U>(rhs)); \ |
| 33 | } |
| 34 | |
| 35 | #pragma push_macro("EXPLICIT_CONVERSION_GENERATE_COMPARISON_OPERATORS") |
| 36 | #undef EXPLICIT_CONVERSION_GENERATE_COMPARISON_OPERATORS |
| 37 | // Generate comparison operator friend functions for types (appropriately |
| 38 | // const/ref qualified) where T is **explicitly** convertible to U. |
| 39 | #define EXPLICIT_CONVERSION_GENERATE_COMPARISON_OPERATORS(T, U) \ |
| 40 | EXPLICIT_CONVERSION_GENERATE_OPERATOR(T, U, ==) \ |
| 41 | EXPLICIT_CONVERSION_GENERATE_OPERATOR(T, U, !=) \ |
| 42 | EXPLICIT_CONVERSION_GENERATE_OPERATOR(T, U, <) \ |
| 43 | EXPLICIT_CONVERSION_GENERATE_OPERATOR(T, U, <=) \ |
| 44 | EXPLICIT_CONVERSION_GENERATE_OPERATOR(T, U, >) \ |
| 45 | EXPLICIT_CONVERSION_GENERATE_OPERATOR(T, U, >=) |
| 46 | |
| 47 | namespace android::mediautils { |
| 48 | |
| 49 | // This class a reference to a string with static storage duration |
| 50 | // which is const (i.e. a string view). We expose an identical API to |
| 51 | // string_view, however we do not publicly inherit to avoid potential mis-use of |
| 52 | // non-virtual dtors/methods. |
| 53 | // |
| 54 | // We can create APIs which consume only static strings, which |
| 55 | // avoids allocation/deallocation of the string locally, as well as potential |
| 56 | // lifetime issues caused by consuming raw pointers (or string_views). |
| 57 | // Equivalently, a string_view which is always valid, and whose underlying data |
| 58 | // can never change. |
| 59 | // |
| 60 | // In most cases, the string_view should be initialized at compile time (and there are |
| 61 | // helpers to do so below). In order to initialize a non-constexpr array, |
| 62 | // the second template param must be false (i.e. opt-in). |
| 63 | // Construction/usage as follows (constexpr required unless second template param is false): |
| 64 | // |
| 65 | // constexpr static std::array<char, 12> debugString = toStdArray("MyMethodName"); |
| 66 | // constexpr auto myStaticStringView = StaticStringView::create<debugString>(); |
| 67 | // const auto size_t length = myStaticStringView.length() // can call any string_view methods |
| 68 | // globalLog(myStaticStringView, ...); // Pass to APIs consuming StaticStringViews |
| 69 | // |
| 70 | struct StaticStringView final : private std::string_view { |
| 71 | template <typename T> |
| 72 | struct is_const_char_array : std::false_type {}; |
| 73 | |
| 74 | // Use templated value helper |
| 75 | template <size_t N> |
| 76 | struct is_const_char_array<const std::array<char, N>> : std::true_type {}; |
| 77 | |
| 78 | template <typename T> |
| 79 | static constexpr bool is_const_char_array_v = |
| 80 | is_const_char_array<std::remove_reference_t<T>>::value; |
| 81 | |
| 82 | template <auto& val, std::enable_if_t<is_const_char_array_v<decltype(val)>, bool> Check = true> |
| 83 | static constexpr StaticStringView create() { |
| 84 | if constexpr (Check) { |
| 85 | // If this static_assert fails to compile, this method was called |
| 86 | // with a non-constexpr |
| 87 | static_assert(val[0]); |
| 88 | } |
| 89 | return StaticStringView{val.data(), val.size()}; |
| 90 | } |
| 91 | |
| 92 | // We can copy/move assign/construct from other StaticStringViews as their validity is already |
| 93 | // ensured |
| 94 | constexpr StaticStringView(const StaticStringView& other) = default; |
| 95 | constexpr StaticStringView& operator=(const StaticStringView& other) = default; |
| 96 | constexpr StaticStringView(StaticStringView&& other) = default; |
| 97 | constexpr StaticStringView& operator=(StaticStringView&& other) = default; |
| 98 | |
| 99 | // Explicitly convert to a std::string_view (this is a strict loss of |
| 100 | // information so should only be used across APIs which intend to consume |
| 101 | // any std::string_view). |
| 102 | constexpr std::string_view getStringView() const { return *this; } |
| 103 | |
| 104 | // The following methods expose an identical API to std::string_view |
| 105 | using std::string_view::begin; |
| 106 | using std::string_view::cbegin; |
| 107 | using std::string_view::cend; |
| 108 | using std::string_view::crbegin; |
| 109 | using std::string_view::crend; |
| 110 | using std::string_view::end; |
| 111 | using std::string_view::rbegin; |
| 112 | using std::string_view::rend; |
| 113 | using std::string_view::operator[]; |
| 114 | using std::string_view::at; |
| 115 | using std::string_view::back; |
| 116 | using std::string_view::data; |
| 117 | using std::string_view::empty; |
| 118 | using std::string_view::front; |
| 119 | using std::string_view::length; |
| 120 | using std::string_view::max_size; |
| 121 | using std::string_view::size; |
| 122 | // These modifiers are valid because the resulting view is a |
| 123 | // substring of the original static string |
| 124 | using std::string_view::remove_prefix; |
| 125 | using std::string_view::remove_suffix; |
| 126 | // Skip swap |
| 127 | using std::string_view::compare; |
| 128 | using std::string_view::copy; |
| 129 | using std::string_view::find; |
| 130 | using std::string_view::find_first_not_of; |
| 131 | using std::string_view::find_first_of; |
| 132 | using std::string_view::find_last_not_of; |
| 133 | using std::string_view::find_last_of; |
| 134 | using std::string_view::rfind; |
| 135 | using std::string_view::substr; |
| 136 | #if __cplusplus >= 202202L |
| 137 | using std::string_view::ends_with; |
| 138 | using std::string_view::starts_with; |
| 139 | #endif |
| 140 | using std::string_view::npos; |
| 141 | |
| 142 | // Non-member friend functions to follow. Identical API to std::string_view |
| 143 | template <class CharT, class Traits> |
| 144 | friend std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, |
| 145 | StaticStringView v) { |
| 146 | return os << static_cast<std::string_view&>(v); |
| 147 | } |
| 148 | |
| 149 | EXPLICIT_CONVERSION_GENERATE_COMPARISON_OPERATORS(const StaticStringView&, |
| 150 | const std::string_view&) |
| 151 | |
| 152 | private: |
| 153 | constexpr StaticStringView(const char* ptr, size_t sz) : std::string_view(ptr, sz){}; |
| 154 | |
| 155 | public: |
| 156 | // The next two functions are logically consteval (only avail in c++20). |
| 157 | // We can't use templates as params, as they would require references to |
| 158 | // static which would unnecessarily bloat executable size. |
| 159 | template <typename T, size_t N, size_t M> |
| 160 | static constexpr std::array<T, N + M> concatArray(const std::array<T, N>& a, |
| 161 | const std::array<T, M>& b) { |
| 162 | std::array<T, N + M> res{}; |
| 163 | for (size_t i = 0; i < N; i++) { |
| 164 | res[i] = a[i]; |
| 165 | } |
| 166 | for (size_t i = 0; i < M; i++) { |
| 167 | res[N + i] = b[i]; |
| 168 | } |
| 169 | return res; |
| 170 | } |
| 171 | |
| 172 | static void arrayIsNotNullTerminated(); |
| 173 | |
| 174 | // This method should only be called on C-style char arrays which are |
| 175 | // null-terminated. Calling this method on a char array with intermediate null |
| 176 | // characters (i.e. "hello\0" or "hel\0lo" will result in a std::array with null |
| 177 | // characters, which is most likely not intended. |
| 178 | // We attempt to detect a non-null terminated char array at link-time, but |
| 179 | // this is best effort. A consequence of this approach is that this method |
| 180 | // will fail to link for extern args, or when not inlined. Since this method |
| 181 | // is intended to be used constexpr, this is not an issue. |
| 182 | template <size_t N> |
| 183 | static constexpr std::array<char, N - 1> toStdArray(const char (&input)[N]) { |
| 184 | std::array<char, N - 1> res{}; |
| 185 | for (size_t i = 0; i < N - 1; i++) { |
| 186 | res[i] = input[i]; |
| 187 | } |
| 188 | // A workaround to generate a link-time error if toStdArray is not called on |
| 189 | // a null-terminated char array. |
| 190 | if (input[N - 1] != 0) arrayIsNotNullTerminated(); |
| 191 | return res; |
| 192 | } |
| 193 | }; |
| 194 | } // namespace android::mediautils |
| 195 | |
| 196 | // Specialization of std::hash for use with std::unordered_map |
| 197 | namespace std { |
| 198 | template <> |
| 199 | struct hash<android::mediautils::StaticStringView> { |
| 200 | constexpr size_t operator()(const android::mediautils::StaticStringView& val) { |
| 201 | return std::hash<std::string_view>{}(val.getStringView()); |
| 202 | } |
| 203 | }; |
| 204 | } // namespace std |
| 205 | |
| 206 | #pragma pop_macro("EXPLICIT_CONVERSION_GENERATE_OPERATOR") |
| 207 | #pragma pop_macro("EXPLICIT_CONVERSION_GENERATE_COMPARISON_OPERATORS") |