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 static_cast<U>(lhs) op static_cast<U>(rhs); \ 27 } \ 28 friend constexpr bool operator op(T lhs, U rhs) { \ 29 return static_cast<U>(lhs) op rhs; \ 30 } \ 31 friend constexpr bool operator op(U lhs, T rhs) { \ 32 return lhs op 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") 208