1 /*
2  * Copyright (C) 2021 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 <memory>
20 #include <string>
21 #include <type_traits>
22 
23 namespace unwindstack {
24 
25 // Ref-counted read-only string.  Used to avoid string allocations/copies.
26 // It is intended to be transparent std::string replacement in most cases.
27 class SharedString {
28  public:
SharedString()29   SharedString() : data_() {}
SharedString(std::string && s)30   SharedString(std::string&& s) : data_(std::make_shared<const std::string>(std::move(s))) {}
SharedString(const std::string & s)31   SharedString(const std::string& s) : SharedString(std::string(s)) {}
SharedString(const char * s)32   SharedString(const char* s) : SharedString(std::string(s)) {}
33 
clear()34   void clear() { data_.reset(); }
is_null()35   bool is_null() const { return data_.get() == nullptr; }
empty()36   bool empty() const { return is_null() ? true : data_->empty(); }
c_str()37   const char* c_str() const { return is_null() ? "" : data_->c_str(); }
38 
39   operator const std::string&() const {
40     [[clang::no_destroy]] static const EmptyString empty;
41     return data_ ? *data_.get() : empty.str;
42   }
43 
string_view()44   operator std::string_view() const { return static_cast<const std::string&>(*this); }
45 
46  private:
47   std::shared_ptr<const std::string> data_;
48 
49   // Wrap the std::string in a struct with a non-constexpr user-declared
50   // constructor, ensuring that the static variable consistently uses dynamic
51   // initialization regardless of whether string() is a constexpr constructor
52   // (i.e. regardless of whether the code is compiled with C++17 or C++20). If
53   // this code were always compiled with C++20, then this struct could be
54   // removed, and the std::string variable could be declared
55   // `[[clang::no_destroy]] static constinit const` See b/330974273.
56   struct EmptyString {
EmptyStringEmptyString57     EmptyString() {}
58     std::string str;
59   };
60 };
61 
62 template <typename T, typename = std::enable_if_t<std::is_same_v<T, SharedString>>>
63 static inline bool operator==(const T& a, const T& b) {
64   return static_cast<std::string_view>(a) == static_cast<std::string_view>(b);
65 }
66 static inline bool operator==(const SharedString& a, std::string_view b) {
67   return static_cast<std::string_view>(a) == b;
68 }
69 static inline bool operator==(std::string_view a, const SharedString& b) {
70   return a == static_cast<std::string_view>(b);
71 }
72 template <typename T, typename = std::enable_if_t<std::is_same_v<T, SharedString>>>
73 static inline bool operator!=(const T& a, const T& b) {
74   return !(a == b);
75 }
76 static inline bool operator!=(const SharedString& a, std::string_view b) {
77   return !(a == b);
78 }
79 static inline bool operator!=(std::string_view a, const SharedString& b) {
80   return !(a == b);
81 }
82 static inline std::string operator+(const SharedString& a, const char* b) {
83   return static_cast<const std::string&>(a) + b;
84 }
85 static inline std::string operator+(const char* a, const SharedString& b) {
86   return a + static_cast<const std::string&>(b);
87 }
88 
89 }  // namespace unwindstack
90