1 // Copyright (c) 2020 The Chromium Authors. All rights reserved. 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 TOOLS_GN_STRING_ATOM_H_ 6 #define TOOLS_GN_STRING_ATOM_H_ 7 8 #include <functional> 9 #include <string> 10 #include <string_view> 11 12 // A StringAtom models a pointer to a globally unique constant string. 13 // 14 // They are useful as key types for sets and map container types, especially 15 // when a program uses multiple instances that tend to use the same strings 16 // (as happen very frequently in GN). 17 // 18 // Note that default equality and comparison functions will compare the 19 // string content, not the pointers, ensuring that the behaviour of 20 // standard containers using StringAtom key types is the same as if 21 // std::string was used. 22 // 23 // In addition, _ordered_ containers support heterogeneous lookups (i.e. 24 // using an std::string_view, and by automatic conversion, a const char* 25 // of const char[] literal) as a key type. 26 // 27 // Additionally, it is also possible to implement very fast _unordered_ 28 // containers by using the StringAtom::Fast{Hash,Equal,Compare} structs, 29 // which will force containers to hash/compare pointer values instead, 30 // for example: 31 // 32 // // A fast unordered set of unique strings. 33 // // 34 // // Implementation uses a hash table so performance will be bounded 35 // // by the string hash function. Does not support heterogeneous lookups. 36 // // 37 // using FastStringSet = std::unordered_set<StringAtom, 38 // StringAtom::PtrHash, 39 // StringAtom::PtrEqual>; 40 // 41 // // A fast unordered set of unique strings. 42 // // 43 // // Implementation uses a balanced binary tree so performance will 44 // // be bounded by string comparisons. Does support heterogeneous lookups, 45 // // but not that this does not extend to the [] operator, only to the 46 // // find() method. 47 // // 48 // using FastStringSet = std::set<StringAtom, StringAtom::PtrCompare> 49 // 50 // // A fast unordered { string -> VALUE } map. 51 // // 52 // // Implementation uses a balanced binary tree. Supports heterogeneous 53 // // lookups. 54 // template <typename VALUE> 55 // using FastStringMap = std::map<StringAtom, VALUE, StringAtom::PtrCompare> 56 // 57 class StringAtom { 58 public: 59 // Default constructor. Value points to a globally unique empty string. 60 StringAtom(); 61 62 // Destructor should do nothing at all. 63 ~StringAtom() = default; 64 65 // Non-explicit constructors. 66 StringAtom(std::string_view str) noexcept; 67 68 // Copy and move operations. StringAtom(const StringAtom & other)69 StringAtom(const StringAtom& other) noexcept : value_(other.value_) {} 70 StringAtom& operator=(const StringAtom& other) noexcept { 71 if (this != &other) { 72 this->~StringAtom(); // really a no-op 73 new (this) StringAtom(other); 74 } 75 return *this; 76 } 77 StringAtom(StringAtom && other)78 StringAtom(StringAtom&& other) noexcept : value_(other.value_) {} 79 StringAtom& operator=(const StringAtom&& other) noexcept { 80 if (this != &other) { 81 this->~StringAtom(); // really a no-op 82 new (this) StringAtom(std::move(other)); 83 } 84 return *this; 85 } 86 empty()87 bool empty() const { return value_.empty(); } 88 89 // Explicit conversions. str()90 const std::string& str() const { return value_; } 91 92 // Implicit conversions. string_view()93 operator std::string_view() const { return {value_}; } 94 95 // Returns true iff this is the same key. 96 // Note that the default comparison functions compare the value instead 97 // in order to use them in standard containers without surprises by 98 // default. SameAs(const StringAtom & other)99 bool SameAs(const StringAtom& other) const { 100 return &value_ == &other.value_; 101 } 102 103 // Default comparison functions. 104 bool operator==(const StringAtom& other) const { 105 return value_ == other.value_; 106 } 107 108 bool operator!=(const StringAtom& other) const { 109 return value_ != other.value_; 110 } 111 112 bool operator<(const StringAtom& other) const { 113 // Avoid one un-necessary string comparison if values are equal. 114 if (SameAs(other)) 115 return false; 116 117 return value_ < other.value_; 118 } 119 hash()120 size_t hash() const { return std::hash<std::string>()(value_); } 121 122 // Use the following method and structs to implement containers that 123 // use StringAtom values as keys, but only compare/hash the pointer 124 // values for speed. 125 // 126 // E.g.: 127 // using FastSet = std::unordered_set<StringAtom, PtrHash, PtrEqual>; 128 // using FastMap = std::map<StringAtom, Value, PtrCompare>; 129 // 130 // IMPORTANT: Note that FastMap above is ordered based in the StringAtom 131 // pointer value, not the string content. 132 // ptr_hash()133 size_t ptr_hash() const { return std::hash<const std::string*>()(&value_); } 134 135 struct PtrHash { operatorPtrHash136 size_t operator()(const StringAtom& key) const noexcept { 137 return key.ptr_hash(); 138 } 139 }; 140 141 struct PtrEqual { operatorPtrEqual142 bool operator()(const StringAtom& a, const StringAtom& b) const noexcept { 143 return &a.value_ == &b.value_; 144 } 145 }; 146 147 struct PtrCompare { operatorPtrCompare148 bool operator()(const StringAtom& a, const StringAtom& b) const noexcept { 149 return &a.value_ < &b.value_; 150 } 151 }; 152 153 protected: 154 const std::string& value_; 155 }; 156 157 namespace std { 158 159 // Ensure default heterogeneous lookups with other types like std::string_view. 160 template <> 161 struct less<StringAtom> { 162 using is_transparent = int; 163 164 bool operator()(const StringAtom& a, const StringAtom& b) const noexcept { 165 return a.str() < b.str(); 166 } 167 template <typename U> 168 bool operator()(const StringAtom& a, const U& b) const noexcept { 169 return a.str() < b; 170 } 171 template <typename U> 172 bool operator()(const U& a, const StringAtom& b) const noexcept { 173 return a < b.str(); 174 }; 175 }; 176 177 template <> 178 struct hash<StringAtom> { 179 size_t operator()(const StringAtom& key) const noexcept { return key.hash(); } 180 }; 181 182 } // namespace std 183 184 #endif // TOOLS_GN_STRING_ATOM_H_ 185