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