• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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