1 #pragma once 2 3 #include <c10/util/ArrayRef.h> 4 #include <c10/util/Exception.h> 5 #include <c10/util/StringUtil.h> 6 #include <c10/util/irange.h> 7 #include <string> 8 9 namespace c10 { 10 11 // Represents a name of the form "foo.bar.baz" 12 struct QualifiedName { 13 QualifiedName() = default; 14 15 // `name` can be a dotted string, like "foo.bar.baz", or just a bare name. QualifiedNameQualifiedName16 /* implicit */ QualifiedName(const std::string& name) { 17 TORCH_CHECK(!name.empty()); 18 // split the string into its atoms. 19 size_t startSearchFrom = 0; 20 size_t pos = name.find(delimiter_, startSearchFrom); 21 22 while (pos != std::string::npos) { 23 auto atom = name.substr(startSearchFrom, pos - startSearchFrom); 24 TORCH_INTERNAL_ASSERT( 25 !atom.empty(), "Invalid name for qualified name: '", name, "'"); 26 atoms_.push_back(std::move(atom)); 27 startSearchFrom = pos + 1; 28 pos = name.find(delimiter_, startSearchFrom); 29 } 30 31 auto finalAtom = name.substr(startSearchFrom); 32 TORCH_INTERNAL_ASSERT( 33 !finalAtom.empty(), "Invalid name for qualified name: '", name, "'"); 34 atoms_.emplace_back(std::move(finalAtom)); 35 36 cacheAccessors(); 37 } 38 QualifiedNameQualifiedName39 explicit QualifiedName(std::vector<std::string> atoms) : atoms_(std::move(atoms)) { 40 for (const auto& atom : atoms_) { 41 TORCH_CHECK(!atom.empty(), "Atom cannot be empty"); 42 TORCH_CHECK( 43 atom.find(delimiter_) == std::string::npos, 44 "Delimiter not allowed in atom"); 45 } 46 47 cacheAccessors(); 48 } 49 // Unnecessary copy. Ideally we'd use something like std::string_view. QualifiedNameQualifiedName50 /* implicit */ QualifiedName(const char* name) 51 : QualifiedName(std::string(name)) {} 52 53 // `name` must be a bare name (no dots!) QualifiedNameQualifiedName54 explicit QualifiedName(const QualifiedName& prefix, std::string name) { 55 TORCH_INTERNAL_ASSERT(!name.empty()); 56 TORCH_INTERNAL_ASSERT(name.find(delimiter_) == std::string::npos); 57 atoms_.insert(atoms_.begin(), prefix.atoms_.begin(), prefix.atoms_.end()); 58 atoms_.push_back(std::move(name)); 59 60 cacheAccessors(); 61 } 62 63 // Is `this` a prefix of `other`? 64 // For example, "foo.bar" is a prefix of "foo.bar.baz" isPrefixOfQualifiedName65 bool isPrefixOf(const QualifiedName& other) const { 66 const auto& thisAtoms = atoms_; 67 const auto& otherAtoms = other.atoms_; 68 69 if (thisAtoms.size() > otherAtoms.size()) { 70 // Can't be a prefix if it's bigger 71 return false; 72 } 73 for (const auto i : c10::irange(thisAtoms.size())) { 74 if (thisAtoms[i] != otherAtoms[i]) { 75 return false; 76 } 77 } 78 return true; 79 } 80 81 // The fully qualified name, like "foo.bar.baz" qualifiedNameQualifiedName82 const std::string& qualifiedName() const { 83 return qualifiedName_; 84 } 85 86 // The leading qualifier, like "foo.bar" prefixQualifiedName87 const std::string& prefix() const { 88 return prefix_; 89 } 90 91 // The base name, like "baz" nameQualifiedName92 const std::string& name() const { 93 return name_; 94 } 95 atomsQualifiedName96 const std::vector<std::string>& atoms() const { 97 return atoms_; 98 } 99 100 bool operator==(const QualifiedName& other) const { 101 return this->qualifiedName_ == other.qualifiedName_; 102 } 103 104 bool operator!=(const QualifiedName& other) const { 105 return !(*this == other); 106 } 107 108 private: 109 static constexpr char delimiter_ = '.'; 110 111 // Helper for cacheAccessors() below. 112 template<typename T> joinQualifiedName113 std::string join(char delimiter, const T& v) { 114 std::string out; 115 size_t reserve = 0; 116 for (const auto& e : v) { 117 reserve += e.size() + 1; 118 } 119 out.reserve(reserve); 120 for (const auto i : c10::irange(v.size())) { 121 if (i != 0) { 122 out.push_back(delimiter); 123 } 124 out.append(v[i]); 125 } 126 return out; 127 } 128 cacheAccessorsQualifiedName129 void cacheAccessors() { 130 qualifiedName_ = join(delimiter_, atoms_); 131 if (atoms_.size() > 1) { 132 ArrayRef<std::string> view(atoms_); 133 const auto prefixView = view.slice(0, view.size() - 1); 134 prefix_ = join(delimiter_, prefixView); 135 } 136 137 if (!atoms_.empty()) { 138 name_ = atoms_.back(); 139 } 140 } 141 142 // The actual list of names, like "{foo, bar, baz}" 143 std::vector<std::string> atoms_; 144 145 /* 146 * Cached accessors, derived from `atoms_`. 147 */ 148 std::string qualifiedName_; 149 std::string prefix_; 150 std::string name_; 151 }; 152 } // namespace c10 153 154 namespace std { 155 template <> 156 struct hash<c10::QualifiedName> { 157 size_t operator()(const c10::QualifiedName& n) const noexcept { 158 return std::hash<std::string>()(n.qualifiedName()); 159 } 160 }; 161 } // namespace std 162